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