diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -263,6 +263,8 @@ protected: // Utility methods for derived classes + lldb::ModuleSP FindModuleViaTarget(const FileSpec &file); + /// Checks to see if the target module has changed, updates the target /// accordingly and returns the target executable module. lldb::ModuleSP GetTargetExecutable(); diff --git a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py --- a/lldb/packages/Python/lldbsuite/test/gdbclientutils.py +++ b/lldb/packages/Python/lldbsuite/test/gdbclientutils.py @@ -66,8 +66,9 @@ """ out = "" hex_len = len(hex_bytes) + i = 0 while i < hex_len - 1: - out += chr(int(hex_bytes[i:i + 2]), 16) + out += chr(int(hex_bytes[i:i + 2], 16)) i += 2 return out @@ -178,6 +179,8 @@ return self.qGetWorkingDir() if packet == "qOffsets": return self.qOffsets(); + if packet == "qProcessInfo": + return self.qProcessInfo() if packet == "qsProcessInfo": return self.qsProcessInfo() if packet.startswith("qfProcessInfo"): @@ -214,6 +217,9 @@ def qOffsets(self): return "" + def qProcessInfo(self): + return "" + def qHostInfo(self): return "ptrsize:8;endian:little;" diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -145,72 +145,31 @@ return sections; } -ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file, - addr_t link_map_addr, - addr_t base_addr, - bool base_addr_is_offset) { +ModuleSP DynamicLoader::FindModuleViaTarget(const FileSpec &file) { Target &target = m_process->GetTarget(); - ModuleList &modules = target.GetImages(); ModuleSpec module_spec(file, target.GetArchitecture()); - ModuleSP module_sp; - if ((module_sp = modules.FindFirstModule(module_spec))) { - UpdateLoadedSections(module_sp, link_map_addr, base_addr, - base_addr_is_offset); + if (ModuleSP module_sp = target.GetImages().FindFirstModule(module_spec)) return module_sp; - } - if ((module_sp = target.GetOrCreateModule(module_spec, - true /* notify */))) { - UpdateLoadedSections(module_sp, link_map_addr, base_addr, - base_addr_is_offset); + if (ModuleSP module_sp = target.GetOrCreateModule(module_spec, + /*notify=*/true)) return module_sp; - } - - bool check_alternative_file_name = true; - if (base_addr_is_offset) { - // Try to fetch the load address of the file from the process as we need - // absolute load address to read the file out of the memory instead of a - // load bias. - bool is_loaded = false; - lldb::addr_t load_addr; - Status error = m_process->GetFileLoadAddress(file, is_loaded, load_addr); - if (error.Success() && is_loaded) { - check_alternative_file_name = false; - base_addr = load_addr; - } - } - - // We failed to find the module based on its name. Lets try to check if we - // can find a different name based on the memory region info. - if (check_alternative_file_name) { - MemoryRegionInfo memory_info; - Status error = m_process->GetMemoryRegionInfo(base_addr, memory_info); - if (error.Success() && memory_info.GetMapped() && - memory_info.GetRange().GetRangeBase() == base_addr && - !(memory_info.GetName().IsEmpty())) { - ModuleSpec new_module_spec(FileSpec(memory_info.GetName().GetStringRef()), - target.GetArchitecture()); - - if ((module_sp = modules.FindFirstModule(new_module_spec))) { - UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); - return module_sp; - } - if ((module_sp = target.GetOrCreateModule(new_module_spec, - true /* notify */))) { - UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); - return module_sp; - } - } - } + return nullptr; +} - if ((module_sp = m_process->ReadModuleFromMemory(file, base_addr))) { - UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); - target.GetImages().AppendIfNeeded(module_sp); +ModuleSP DynamicLoader::LoadModuleAtAddress(const FileSpec &file, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + if (ModuleSP module_sp = FindModuleViaTarget(file)) { + UpdateLoadedSections(module_sp, link_map_addr, base_addr, + base_addr_is_offset); + return module_sp; } - return module_sp; + return nullptr; } int64_t DynamicLoader::ReadUnsignedIntWithSizeInBytes(addr_t addr, diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -55,6 +55,11 @@ // PluginInterface protocol llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + lldb::ModuleSP LoadModuleAtAddress(const lldb_private::FileSpec &file, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + protected: /// Runtime linker rendezvous structure. DYLDRendezvous m_rendezvous; diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -575,6 +575,38 @@ return nullptr; } +ModuleSP DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, + addr_t link_map_addr, + addr_t base_addr, + bool base_addr_is_offset) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + file, link_map_addr, base_addr, base_addr_is_offset)) + return module_sp; + + // This works around an dynamic linker "bug" on android <= 23, where the + // dynamic linker would report the application name + // (e.g. com.example.myapplication) instead of the main process binary + // (/system/bin/app_process(32)). The logic is not sound in general (it + // assumes base_addr is the real address, even though it actually is a load + // bias), but it happens to work on adroid because app_process has a file + // address of zero. + // This should be removed after we drop support for android-23. + if (m_process->GetTarget().GetArchitecture().GetTriple().isAndroid()) { + MemoryRegionInfo memory_info; + Status error = m_process->GetMemoryRegionInfo(base_addr, memory_info); + if (error.Success() && memory_info.GetMapped() && + memory_info.GetRange().GetRangeBase() == base_addr && + !(memory_info.GetName().IsEmpty())) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + FileSpec(memory_info.GetName().GetStringRef()), link_map_addr, + base_addr, base_addr_is_offset)) + return module_sp; + } + } + + return nullptr; +} + void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { DYLDRendezvous::iterator I; DYLDRendezvous::iterator E; diff --git a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h --- a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.h @@ -33,6 +33,11 @@ Status CanLoadImage() override { return Status(); } lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread, bool stop) override; + lldb::ModuleSP LoadModuleAtAddress(const lldb_private::FileSpec &file, + lldb::addr_t link_map_addr, + lldb::addr_t base_addr, + bool base_addr_is_offset) override; + /// \} /// PluginInterface protocol. diff --git a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp --- a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp @@ -64,3 +64,19 @@ bool stop) { return ThreadPlanSP(); } + +lldb::ModuleSP DynamicLoaderWasmDYLD::LoadModuleAtAddress( + const lldb_private::FileSpec &file, lldb::addr_t link_map_addr, + lldb::addr_t base_addr, bool base_addr_is_offset) { + if (ModuleSP module_sp = DynamicLoader::LoadModuleAtAddress( + file, link_map_addr, base_addr, base_addr_is_offset)) + return module_sp; + + if (ModuleSP module_sp = m_process->ReadModuleFromMemory(file, base_addr)) { + UpdateLoadedSections(module_sp, link_map_addr, base_addr, false); + m_process->GetTarget().GetImages().AppendIfNeeded(module_sp); + return module_sp; + } + + return nullptr; +} diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestGdbClientModuleLoad.py b/lldb/test/API/functionalities/gdb_remote_client/TestGdbClientModuleLoad.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/TestGdbClientModuleLoad.py @@ -0,0 +1,133 @@ +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test.gdbclientutils import * +from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase +from lldbsuite.support import seven + +class MyResponder(MockGDBServerResponder): + """ + A responder which simulates a process with a single shared library loaded. + Its parameters allow configuration of various properties of the library. + """ + + def __init__(self, testcase, triple, library_name, auxv_entry, region_info): + MockGDBServerResponder.__init__(self) + self.testcase = testcase + self._triple = triple + self._library_name = library_name + self._auxv_entry = auxv_entry + self._region_info = region_info + + def qSupported(self, client_supported): + return (super().qSupported(client_supported) + + ";qXfer:auxv:read+;qXfer:libraries-svr4:read+") + + def qXferRead(self, obj, annex, offset, length): + if obj == "features" and annex == "target.xml": + return """ + + i386:x86-64 + + + + """, False + elif obj == "auxv": + # 0x09 = AT_ENTRY, which lldb uses to compute the load bias of the + # main binary. + return hex_decode_bytes(self._auxv_entry + + "09000000000000000000ee000000000000000000000000000000000000000000"), False + elif obj == "libraries-svr4": + return """ + + + """ % self._library_name, False + else: + return None, False + + def qfThreadInfo(self): + return "m47" + + def qsThreadInfo(self): + return "l" + + def qProcessInfo(self): + return "pid:47;ptrsize:8;endian:little;triple:%s;" % hex_encode_bytes(self._triple) + + def setBreakpoint(self, packet): + return "OK" + + def readMemory(self, addr, length): + if addr == 0xee1000: + return "00"*0x30 + "0020ee0000000000" + elif addr == 0xee2000: + return "01000000000000000030ee0000000000dead00000000000000000000000000000000000000000000" + elif addr == 0xef0000: + with open(self.testcase.getBuildArtifact("libmodule_load.so"), "rb") as f: + contents = f.read(-1) + return hex_encode_bytes(seven.bitcast_to_string(contents)) + return ("baadf00d00"*1000)[0:length*2] + + def qMemoryRegionInfo(self, addr): + if addr < 0xee0000: + return "start:0;size:ee0000;" + elif addr < 0xef0000: + return "start:ee0000;size:10000;" + elif addr < 0xf00000: + return "start:ef0000;size:1000;permissions:rx;" + self._region_info + else: + return "start:ef1000;size:ffffffffff10f000" + +class TestGdbClientModuleLoad(GDBRemoteTestBase): + + mydir = TestBase.compute_mydir(__file__) + + @skipIfXmlSupportMissing + def test_android_app_process(self): + """ + This test simulates the scenario where the (android) dynamic linker + reports incorrect file name of the main executable. Lldb uses + qMemoryRegionInfo to get the correct value. + """ + region_info = "name:%s;" % ( + hex_encode_bytes(self.getBuildArtifact("libmodule_load.so"))) + self.server.responder = MyResponder(self, "x86_64-pc-linux-android", + "bogus-name", "", region_info) + self.yaml2obj("module_load.yaml", self.getBuildArtifact("libmodule_load.so")) + target = self.createTarget("module_load.yaml") + + process = self.connect(target) + self.assertTrue(process.IsValid(), "Process is valid") + + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateStopped]) + + self.filecheck("image list", __file__, "-check-prefix=ANDROID") +# ANDROID: [ 0] {{.*}} 0x0000000000ee0000 {{.*}}module_load +# ANDROID: [ 1] {{.*}} 0x0000000000ef0000 {{.*}}libmodule_load.so + + @skipIfXmlSupportMissing + def test_vdso(self): + """ + This test checks vdso loading in the situation where the process does + not have memory region information about the vdso address. This can + happen in core files, as they don't store this data. + We want to check that the vdso is loaded exactly once. + """ + # vdso address + AT_SYSINFO_EHDR = "21000000000000000000ef0000000000" + self.server.responder = MyResponder(self, "x86_64-pc-linux", + "linux-vdso.so.1", AT_SYSINFO_EHDR, "") + self.yaml2obj("module_load.yaml", self.getBuildArtifact("libmodule_load.so")) + target = self.createTarget("module_load.yaml") + + process = self.connect(target) + self.assertTrue(process.IsValid(), "Process is valid") + + lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, + [lldb.eStateStopped]) + + self.filecheck("image list", __file__, "-check-prefix=VDSO") +# VDSO: [ 0] {{.*}} 0x0000000000ee0000 {{.*}}module_load +# VDSO: [ 1] {{.*}} 0x0000000000ef0000 {{.*}}[vdso] + self.assertEquals(self.target().GetNumModules(), 2) diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -32,8 +32,6 @@ MockGDBServerResponder.__init__(self) def respond(self, packet): - if packet == "qProcessInfo": - return self.qProcessInfo() if packet[0:13] == "qRegisterInfo": return self.qRegisterInfo(packet[13:]) return MockGDBServerResponder.respond(self, packet) diff --git a/lldb/test/API/functionalities/gdb_remote_client/module_load.yaml b/lldb/test/API/functionalities/gdb_remote_client/module_load.yaml new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/module_load.yaml @@ -0,0 +1,53 @@ +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0000 + AddressAlign: 0x4 + Content: "c3c3c3c3" + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x1000 + AddressAlign: 0x4 + Size: 0x28 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x8 + Entries: + - Tag: DT_DEBUG + Value: 0xdead0000 + +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x0000 + Align: 0x4 + FirstSec: .text + LastSec: .text + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x1000 + Align: 0x4 + FirstSec: .data + LastSec: .dynamic + - Type: PT_DYNAMIC + Flags: [ PF_W, PF_R ] + VAddr: 0x1028 + FirstSec: .dynamic + LastSec: .dynamic +DynamicSymbols: + - Name: _r_debug + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x0 + Size: 0x28 +