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