When opening core files (and also in some other situations) we could end up with two vdso modules. This could happen because the vdso module is very special, and over the years, we have accumulated various ways to load it. In D10800, we added one mechanism for loading it, which took the form of a generic load-from-memory capability. Unfortunately loading an elf file from memory is not possible (because the loader never loads the entire file), and our attempts to do so were causing crashes. So, in D34352, we partially reverted D10800 and implemented a custom mechanism specific to the vdso. Unfortunately, enough of D10800 remained such that, under the right circumstances, it could end up loading a second (non-functional) copy of the vdso module. This happened when the process plugin did not support the extended MemoryRegionInfo query (added in D22219, to workaround a different bug), which meant that the loader plugin was not able to recognise that the linux-vdso.so.1 module (this is how the loader calls it) is in fact the same as the [vdso] module (the name used in /proc/$PID/maps) we loaded before. This typically happened in a core file, as they don't store this kind of information. This patch fixes the issue by completing the revert of D10800 -- the memory loading code is removed completely. It also reduces the scope of the hackaround introduced in D22219 -- it isn't completely sound and is only relevant for fairly old (but still supported) versions of android. I added the memory loading logic to the wasm dynamic loader, which has since appeared and is relying on this feature (it even has a test). As far as I can tell loading wasm modules from memory is possible and reliable. MachO memory loading is not affected by this patch, as it uses a completely different code path. Since the scenarios/patches I described came without test cases, I have created two new gdb-client tests cases for them. They're not particularly readable, but right now, this is the best way we can simulate the behavior (bugs) of a particular dynamic linker. Differential Revision: https://reviews.llvm.org/D122660
229 lines
9.1 KiB
Python
229 lines
9.1 KiB
Python
import lldb
|
|
import binascii
|
|
from lldbsuite.test.lldbtest import *
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.gdbclientutils import *
|
|
from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
|
|
|
|
LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS
|
|
load_address = 0x400000000
|
|
|
|
def format_register_value(val):
|
|
"""
|
|
Encode each byte by two hex digits in little-endian order.
|
|
"""
|
|
result = ""
|
|
mask = 0xff
|
|
shift = 0
|
|
for i in range(0, 8):
|
|
x = (val & mask) >> shift
|
|
result += format(x, '02x')
|
|
mask <<= 8
|
|
shift += 8
|
|
return result
|
|
|
|
|
|
class MyResponder(MockGDBServerResponder):
|
|
current_pc = load_address + 0x0a
|
|
|
|
def __init__(self, obj_path, module_name = ""):
|
|
self._obj_path = obj_path
|
|
self._module_name = module_name or obj_path
|
|
MockGDBServerResponder.__init__(self)
|
|
|
|
def respond(self, packet):
|
|
if packet[0:13] == "qRegisterInfo":
|
|
return self.qRegisterInfo(packet[13:])
|
|
return MockGDBServerResponder.respond(self, packet)
|
|
|
|
def qSupported(self, client_supported):
|
|
return "qXfer:libraries:read+;PacketSize=1000;vContSupported-"
|
|
|
|
def qHostInfo(self):
|
|
return ""
|
|
|
|
def QEnableErrorStrings(self):
|
|
return ""
|
|
|
|
def qfThreadInfo(self):
|
|
return "OK"
|
|
|
|
def qRegisterInfo(self, index):
|
|
if (index == 0):
|
|
return "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;"
|
|
return "E45"
|
|
|
|
def qProcessInfo(self):
|
|
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"))
|
|
|
|
def haltReason(self):
|
|
return "T05thread:1;"
|
|
|
|
def readRegister(self, register):
|
|
return format_register_value(self.current_pc)
|
|
|
|
def qXferRead(self, obj, annex, offset, length):
|
|
if obj == "libraries":
|
|
xml = '<library-list><library name=\"%s\"><section address=\"%d\"/></library></library-list>' % (self._module_name, load_address)
|
|
return xml, False
|
|
else:
|
|
return None, False
|
|
|
|
def readMemory(self, addr, length):
|
|
if addr < load_address:
|
|
return "E02"
|
|
result = ""
|
|
with open(self._obj_path, mode='rb') as file:
|
|
file_content = bytearray(file.read())
|
|
addr_from = addr - load_address
|
|
addr_to = addr_from + min(length, len(file_content) - addr_from)
|
|
for i in range(addr_from, addr_to):
|
|
result += format(file_content[i], '02x')
|
|
file.close()
|
|
return result
|
|
|
|
|
|
class TestWasm(GDBRemoteTestBase):
|
|
|
|
mydir = TestBase.compute_mydir(__file__)
|
|
|
|
@skipIfAsan
|
|
@skipIfXmlSupportMissing
|
|
def test_load_module_with_embedded_symbols_from_remote(self):
|
|
"""Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with embedded DWARF symbols"""
|
|
|
|
yaml_path = "test_wasm_embedded_debug_sections.yaml"
|
|
yaml_base, ext = os.path.splitext(yaml_path)
|
|
obj_path = self.getBuildArtifact(yaml_base)
|
|
self.yaml2obj(yaml_path, obj_path)
|
|
|
|
self.server.responder = MyResponder(obj_path, "test_wasm")
|
|
|
|
target = self.dbg.CreateTarget("")
|
|
process = self.connect(target)
|
|
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
|
|
|
|
num_modules = target.GetNumModules()
|
|
self.assertEquals(1, num_modules)
|
|
|
|
module = target.GetModuleAtIndex(0)
|
|
num_sections = module.GetNumSections()
|
|
self.assertEquals(5, num_sections)
|
|
|
|
code_section = module.GetSectionAtIndex(0)
|
|
self.assertEquals("code", code_section.GetName())
|
|
self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
|
|
|
|
debug_info_section = module.GetSectionAtIndex(1)
|
|
self.assertEquals(".debug_info", debug_info_section.GetName())
|
|
self.assertEquals(load_address | debug_info_section.GetFileOffset(), debug_info_section.GetLoadAddress(target))
|
|
|
|
debug_abbrev_section = module.GetSectionAtIndex(2)
|
|
self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
|
|
self.assertEquals(load_address | debug_abbrev_section.GetFileOffset(), debug_abbrev_section.GetLoadAddress(target))
|
|
|
|
debug_line_section = module.GetSectionAtIndex(3)
|
|
self.assertEquals(".debug_line", debug_line_section.GetName())
|
|
self.assertEquals(load_address | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target))
|
|
|
|
debug_str_section = module.GetSectionAtIndex(4)
|
|
self.assertEquals(".debug_str", debug_str_section.GetName())
|
|
self.assertEquals(load_address | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target))
|
|
|
|
|
|
@skipIfAsan
|
|
@skipIfXmlSupportMissing
|
|
def test_load_module_with_stripped_symbols_from_remote(self):
|
|
"""Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with symbols stripped into a separate Wasm file"""
|
|
|
|
sym_yaml_path = "test_sym.yaml"
|
|
sym_yaml_base, ext = os.path.splitext(sym_yaml_path)
|
|
sym_obj_path = self.getBuildArtifact(sym_yaml_base) + ".wasm"
|
|
self.yaml2obj(sym_yaml_path, sym_obj_path)
|
|
|
|
yaml_path = "test_wasm_external_debug_sections.yaml"
|
|
yaml_base, ext = os.path.splitext(yaml_path)
|
|
obj_path = self.getBuildArtifact(yaml_base) + ".wasm"
|
|
self.yaml2obj(yaml_path, obj_path)
|
|
|
|
self.server.responder = MyResponder(obj_path, "test_wasm")
|
|
|
|
folder, _ = os.path.split(obj_path)
|
|
self.runCmd("settings set target.debug-file-search-paths " + os.path.abspath(folder))
|
|
|
|
target = self.dbg.CreateTarget("")
|
|
process = self.connect(target)
|
|
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
|
|
|
|
num_modules = target.GetNumModules()
|
|
self.assertEquals(1, num_modules)
|
|
|
|
module = target.GetModuleAtIndex(0)
|
|
num_sections = module.GetNumSections()
|
|
self.assertEquals(5, num_sections)
|
|
|
|
code_section = module.GetSectionAtIndex(0)
|
|
self.assertEquals("code", code_section.GetName())
|
|
self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
|
|
|
|
debug_info_section = module.GetSectionAtIndex(1)
|
|
self.assertEquals(".debug_info", debug_info_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target))
|
|
|
|
debug_abbrev_section = module.GetSectionAtIndex(2)
|
|
self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target))
|
|
|
|
debug_line_section = module.GetSectionAtIndex(3)
|
|
self.assertEquals(".debug_line", debug_line_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
|
|
|
|
debug_str_section = module.GetSectionAtIndex(4)
|
|
self.assertEquals(".debug_str", debug_str_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
|
|
|
|
|
|
@skipIfAsan
|
|
@skipIfXmlSupportMissing
|
|
def test_load_module_from_file(self):
|
|
"""Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module from a file"""
|
|
|
|
yaml_path = "test_wasm_embedded_debug_sections.yaml"
|
|
yaml_base, ext = os.path.splitext(yaml_path)
|
|
obj_path = self.getBuildArtifact(yaml_base)
|
|
self.yaml2obj(yaml_path, obj_path)
|
|
|
|
self.server.responder = MyResponder(obj_path)
|
|
|
|
target = self.dbg.CreateTarget("")
|
|
process = self.connect(target)
|
|
lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateStopped])
|
|
|
|
num_modules = target.GetNumModules()
|
|
self.assertEquals(1, num_modules)
|
|
|
|
module = target.GetModuleAtIndex(0)
|
|
num_sections = module.GetNumSections()
|
|
self.assertEquals(5, num_sections)
|
|
|
|
code_section = module.GetSectionAtIndex(0)
|
|
self.assertEquals("code", code_section.GetName())
|
|
self.assertEquals(load_address | code_section.GetFileOffset(), code_section.GetLoadAddress(target))
|
|
|
|
debug_info_section = module.GetSectionAtIndex(1)
|
|
self.assertEquals(".debug_info", debug_info_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target))
|
|
|
|
debug_abbrev_section = module.GetSectionAtIndex(2)
|
|
self.assertEquals(".debug_abbrev", debug_abbrev_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target))
|
|
|
|
debug_line_section = module.GetSectionAtIndex(3)
|
|
self.assertEquals(".debug_line", debug_line_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
|
|
|
|
debug_str_section = module.GetSectionAtIndex(4)
|
|
self.assertEquals(".debug_str", debug_str_section.GetName())
|
|
self.assertEquals(LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target))
|
|
|