xref: /llvm-project/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py (revision e67cee09499cbf386334115a627cd4fbe19cb01c)
1import lldb
2import binascii
3from lldbsuite.test.lldbtest import *
4from lldbsuite.test.decorators import *
5from lldbsuite.test.gdbclientutils import *
6from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
7
8LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS
9load_address = 0x400000000
10
11def format_register_value(val):
12    """
13    Encode each byte by two hex digits in little-endian order.
14    """
15    result = ""
16    mask = 0xff
17    shift = 0
18    for i in range(0, 8):
19        x = (val & mask) >> shift
20        result += format(x, '02x')
21        mask <<= 8
22        shift += 8
23    return result
24
25
26class MyResponder(MockGDBServerResponder):
27    current_pc = load_address + 0x0a
28
29    def __init__(self, obj_path, module_name = ""):
30        self._obj_path = obj_path
31        self._module_name = module_name or obj_path
32        MockGDBServerResponder.__init__(self)
33
34    def respond(self, packet):
35        if packet[0:13] == "qRegisterInfo":
36            return self.qRegisterInfo(packet[13:])
37        return MockGDBServerResponder.respond(self, packet)
38
39    def qSupported(self, client_supported):
40        return "qXfer:libraries:read+;PacketSize=1000;vContSupported-"
41
42    def qHostInfo(self):
43        return ""
44
45    def QEnableErrorStrings(self):
46        return ""
47
48    def qfThreadInfo(self):
49        return "OK"
50
51    def qRegisterInfo(self, index):
52        if (index == 0):
53            return "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;"
54        return "E45"
55
56    def qProcessInfo(self):
57        return "pid:1;ppid:1;uid:1;gid:1;euid:1;egid:1;name:%s;triple:%s;ptrsize:4" % (hex_encode_bytes("lldb"), hex_encode_bytes("wasm32-unknown-unknown-wasm"))
58
59    def haltReason(self):
60        return "T05thread:1;"
61
62    def readRegister(self, register):
63        return format_register_value(self.current_pc)
64
65    def qXferRead(self, obj, annex, offset, length):
66        if obj == "libraries":
67            xml = '<library-list><library name=\"%s\"><section address=\"%d\"/></library></library-list>' % (self._module_name, load_address)
68            return xml, False
69        else:
70            return None, False
71
72    def readMemory(self, addr, length):
73        if addr < load_address:
74            return "E02"
75        result = ""
76        with open(self._obj_path, mode='rb') as file:
77            file_content = bytearray(file.read())
78            addr_from = addr - load_address
79            addr_to = addr_from + min(length, len(file_content) - addr_from)
80            for i in range(addr_from, addr_to):
81                result += format(file_content[i], '02x')
82            file.close()
83        return result
84
85
86class TestWasm(GDBRemoteTestBase):
87
88    mydir = TestBase.compute_mydir(__file__)
89
90    @skipIfAsan
91    @skipIfXmlSupportMissing
92    def test_load_module_with_embedded_symbols_from_remote(self):
93        """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with embedded DWARF symbols"""
94
95        yaml_path = "test_wasm_embedded_debug_sections.yaml"
96        yaml_base, ext = os.path.splitext(yaml_path)
97        obj_path = self.getBuildArtifact(yaml_base)
98        self.yaml2obj(yaml_path, obj_path)
99
100        self.server.responder = MyResponder(obj_path, "test_wasm")
101
102        target = self.dbg.CreateTarget("")
103        process = self.connect(target)
104        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
105
106        num_modules = target.GetNumModules()
107        self.assertEquals(1, num_modules)
108
109        module = target.GetModuleAtIndex(0)
110        num_sections = module.GetNumSections()
111        self.assertEquals(5, num_sections)
112
113        code_section = module.GetSectionAtIndex(0)
114        self.assertEquals("code", code_section.GetName())
115        self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
116
117        debug_info_section = module.GetSectionAtIndex(1)
118        self.assertEquals(".debug_info", debug_info_section.GetName())
119        self.assertEquals(load_address | debug_info_section.GetFileOffset(), debug_info_section.GetLoadAddress(target))
120
121        debug_abbrev_section = module.GetSectionAtIndex(2)
122        self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
123        self.assertEquals(load_address | debug_abbrev_section.GetFileOffset(), debug_abbrev_section.GetLoadAddress(target))
124
125        debug_line_section = module.GetSectionAtIndex(3)
126        self.assertEquals(".debug_line", debug_line_section.GetName())
127        self.assertEquals(load_address | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target))
128
129        debug_str_section = module.GetSectionAtIndex(4)
130        self.assertEquals(".debug_str", debug_str_section.GetName())
131        self.assertEquals(load_address | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target))
132
133
134    @skipIfAsan
135    @skipIfXmlSupportMissing
136    def test_load_module_with_stripped_symbols_from_remote(self):
137        """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with symbols stripped into a separate Wasm file"""
138
139        sym_yaml_path = "test_sym.yaml"
140        sym_yaml_base, ext = os.path.splitext(sym_yaml_path)
141        sym_obj_path = self.getBuildArtifact(sym_yaml_base) + ".wasm"
142        self.yaml2obj(sym_yaml_path, sym_obj_path)
143
144        yaml_path = "test_wasm_external_debug_sections.yaml"
145        yaml_base, ext = os.path.splitext(yaml_path)
146        obj_path = self.getBuildArtifact(yaml_base) + ".wasm"
147        self.yaml2obj(yaml_path, obj_path)
148
149        self.server.responder = MyResponder(obj_path, "test_wasm")
150
151        folder, _ = os.path.split(obj_path)
152        self.runCmd("settings set target.debug-file-search-paths " + os.path.abspath(folder))
153
154        target = self.dbg.CreateTarget("")
155        process = self.connect(target)
156        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
157
158        num_modules = target.GetNumModules()
159        self.assertEquals(1, num_modules)
160
161        module = target.GetModuleAtIndex(0)
162        num_sections = module.GetNumSections()
163        self.assertEquals(5, num_sections)
164
165        code_section = module.GetSectionAtIndex(0)
166        self.assertEquals("code", code_section.GetName())
167        self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
168
169        debug_info_section = module.GetSectionAtIndex(1)
170        self.assertEquals(".debug_info", debug_info_section.GetName())
171        self.assertEquals(LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target))
172
173        debug_abbrev_section = module.GetSectionAtIndex(2)
174        self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
175        self.assertEquals(LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target))
176
177        debug_line_section = module.GetSectionAtIndex(3)
178        self.assertEquals(".debug_line", debug_line_section.GetName())
179        self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
180
181        debug_str_section = module.GetSectionAtIndex(4)
182        self.assertEquals(".debug_str", debug_str_section.GetName())
183        self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
184
185
186    @skipIfAsan
187    @skipIfXmlSupportMissing
188    def test_load_module_from_file(self):
189        """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module from a file"""
190
191        yaml_path = "test_wasm_embedded_debug_sections.yaml"
192        yaml_base, ext = os.path.splitext(yaml_path)
193        obj_path = self.getBuildArtifact(yaml_base)
194        self.yaml2obj(yaml_path, obj_path)
195
196        self.server.responder = MyResponder(obj_path)
197
198        target = self.dbg.CreateTarget("")
199        process = self.connect(target)
200        lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
201
202        num_modules = target.GetNumModules()
203        self.assertEquals(1, num_modules)
204
205        module = target.GetModuleAtIndex(0)
206        num_sections = module.GetNumSections()
207        self.assertEquals(5, num_sections)
208
209        code_section = module.GetSectionAtIndex(0)
210        self.assertEquals("code", code_section.GetName())
211        self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
212
213        debug_info_section = module.GetSectionAtIndex(1)
214        self.assertEquals(".debug_info", debug_info_section.GetName())
215        self.assertEquals(LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target))
216
217        debug_abbrev_section = module.GetSectionAtIndex(2)
218        self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
219        self.assertEquals(LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target))
220
221        debug_line_section = module.GetSectionAtIndex(3)
222        self.assertEquals(".debug_line", debug_line_section.GetName())
223        self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
224
225        debug_str_section = module.GetSectionAtIndex(4)
226        self.assertEquals(".debug_str", debug_str_section.GetName())
227        self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
228
229