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