llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-lldb Author: Greg Clayton (clayborg) <details> <summary>Changes</summary> This patch enables ELF core files to be loaded and still show executables and shared libraries. Functionality includes: - Load executable and shared libraries from memory if ELF headers are available - Create placeholder for missing shared libraries and executable. Previously you just wouldn't get anything in the "image list" if no executable was provided. --- Full diff: https://github.com/llvm/llvm-project/pull/177289.diff 6 Files Affected: - (modified) lldb/source/Core/DynamicLoader.cpp (+9-5) - (modified) lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp (+15-3) - (modified) lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp (+33-4) - (modified) lldb/source/Plugins/Process/elf-core/ProcessElfCore.h (+2-2) - (modified) lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py (+68) - (added) lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core () ``````````diff diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp index 31d277bc19681..563a81fb8239b 100644 --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -176,13 +176,17 @@ 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)) { + ModuleSP module_sp = FindModuleViaTarget(file); + // We have a core file, try to load the image from memory if we didn't find + // the module. + if (!module_sp && !m_process->IsLiveDebugSession()) { + module_sp = m_process->ReadModuleFromMemory(file, base_addr); + m_process->GetTarget().GetImages().AppendIfNeeded(module_sp, false); + } + if (module_sp) UpdateLoadedSections(module_sp, link_map_addr, base_addr, base_addr_is_offset); - return module_sp; - } - - return nullptr; + return module_sp; } static ModuleSP ReadUnnamedMemoryModule(Process *process, addr_t addr, diff --git a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp index 3605e7b2c6960..1a0ae9378fd80 100644 --- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -27,6 +27,7 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/ProcessInfo.h" #include "llvm/Support/ThreadPool.h" +#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h" #include <memory> #include <optional> @@ -698,22 +699,33 @@ void DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() { ModuleSP executable = GetTargetExecutable(); SetLoadedModule(executable, m_rendezvous.GetLinkMapAddress()); + Target &target = m_process->GetTarget(); std::vector<FileSpec> module_names; for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) module_names.push_back(I->file_spec); m_process->PrefetchModuleSpecs( - module_names, m_process->GetTarget().GetArchitecture().GetTriple()); + module_names, target.GetArchitecture().GetTriple()); - auto load_module_fn = [this, &module_list, + auto load_module_fn = [this, &module_list, &target, &log](const DYLDRendezvous::SOEntry &so_entry) { ModuleSP module_sp = LoadModuleAtAddress( so_entry.file_spec, so_entry.link_addr, so_entry.base_addr, true); + if (!module_sp && !m_process->IsLiveDebugSession()) { + ModuleSpec module_spec(so_entry.file_spec, target.GetArchitecture()); + if (UUID uuid = m_process->FindModuleUUID(so_entry.file_spec.GetPath())) + module_spec.GetUUID() = uuid; + module_sp = Module::CreateModuleFromObjectFile<ObjectFilePlaceholder>( + module_spec, so_entry.base_addr, 512); + bool load_addr_changed = false; + target.GetImages().Append(module_sp, false); + module_sp->SetLoadAddress(target, so_entry.base_addr, + false, load_addr_changed); + } if (module_sp.get()) { LLDB_LOG(log, "LoadAllCurrentModules loading module: {0}", so_entry.file_spec.GetFilename()); module_list.Append(module_sp); } else { - Log *log = GetLog(LLDBLog::DynamicLoader); LLDB_LOGF( log, "DynamicLoaderPOSIXDYLD::%s failed loading module %s at 0x%" PRIx64, diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp index f8e33eac614a4..5e4c67af059db 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -259,7 +259,7 @@ Status ProcessElfCore::DoLoadCore() { lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); if (!exe_module_sp) { if (!m_nt_file_entries.empty()) { - llvm::StringRef executable_path = GetMainExecutablePath(); + std::string executable_path = GetMainExecutablePath(); ModuleSpec exe_module_spec; exe_module_spec.GetArchitecture() = arch; exe_module_spec.GetUUID() = FindModuleUUID(executable_path); @@ -268,6 +268,24 @@ Status ProcessElfCore::DoLoadCore() { if (exe_module_spec.GetFileSpec()) { exe_module_sp = GetTarget().GetOrCreateModule(exe_module_spec, true /* notify */); + if (!exe_module_sp) { + // Create an ELF file from memory for the main executable. The dynamic + // loader requires the main executable so that it can extract the + // DT_DEBUG key/value pair from the dynamic section and get the list + // of shared libraries. + std::optional<lldb::addr_t> exe_header_addr; + + // We need to find its load address + for (const NT_FILE_Entry &file_entry : m_nt_file_entries) { + if (file_entry.path == executable_path) { + exe_header_addr = file_entry.start; + break; + } + } + if (exe_header_addr.has_value()) + exe_module_sp = ReadModuleFromMemory(exe_module_spec.GetFileSpec(), + *exe_header_addr); + } if (exe_module_sp) GetTarget().SetExecutableModule(exe_module_sp, eLoadDependentsNo); } @@ -293,12 +311,23 @@ void ProcessElfCore::UpdateBuildIdForNTFileEntries() { } } -llvm::StringRef ProcessElfCore::GetMainExecutablePath() { +std::string ProcessElfCore::GetMainExecutablePath() { + // Always try to read the program name from core file memory first via the + // AUXV_AT_EXECFN entry. This value is the address of a null terminated C + // string that contains the program path. + AuxVector aux_vector(m_auxv); + std::string execfn_str; + if (auto execfn = aux_vector.GetAuxValue(AuxVector::AUXV_AT_EXECFN)) { + Status error; + if (ReadCStringFromMemory(*execfn, execfn_str, error)) + return execfn_str; + } + if (m_nt_file_entries.empty()) - return ""; + return {}; // The first entry in the NT_FILE might be our executable - llvm::StringRef executable_path = m_nt_file_entries[0].path; + std::string executable_path = m_nt_file_entries[0].path; // Prefer the NT_FILE entry matching m_executable_name as main executable. for (const NT_FILE_Entry &file_entry : m_nt_file_entries) if (llvm::StringRef(file_entry.path).ends_with("/" + m_executable_name)) { diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h index 576c6858477a6..7eda33be8634c 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h @@ -168,8 +168,8 @@ class ProcessElfCore : public lldb_private::PostMortemProcess { lldb_private::UUID FindModuleUUID(const llvm::StringRef path) override; - // Returns the main executable path - llvm::StringRef GetMainExecutablePath(); + // Returns the main executable path. + std::string GetMainExecutablePath(); // Returns the value of certain type of note of a given start address lldb_private::UUID FindBuidIdInCoreMemory(lldb::addr_t address); diff --git a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py index e9403b56ae195..84c20f4c5a4e1 100644 --- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py +++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py @@ -1042,6 +1042,74 @@ def test_read_only_cstring(self): cstr = var.GetSummary() self.assertEqual(cstr, '"_start"') + @skipIfLLVMTargetMissing("X86") + @skipIfWindows + def test_linux_no_exe(self): + """ + Test that we are able to get the shared library list when loading a + linux core file without an executable. This tests LLDB's ability to + create memory object files when the ELF header is available for the + binary in the shared library list, and to create place holder object + files for any files we weren't able to locate or load from memory. It + also tests the dynamic loader's ability to find the list of shared + libraries from the PT_DYNAMIC section's DT_DEBUG entry. The core file + used in this test has the ELF header for the main executable "elf-crash" + and for "/lib64/libm.so.6". This test will verify that all shared + libraries are available. The "image list" output should look like: + + (lldb) image list + [ 0] 7BCC1101 0x000055bb04288000 /data/users/gclayton/args/elf-crash (0x000055bb04288000) + [ 1] 0x00007f27db200000 /lib64/libstdc++.so.6 + [ 2] AF275675-4671-8B49-24C8-A9A657D74115-C80DEE65 0x00007f27db51b000 /lib64/libm.so.6 (0x00007f27db51b000) + [ 3] 0x00007f27db4fe000 /lib64/libgcc_s.so.1 + [ 4] 0x00007f27dae00000 /lib64/libc.so.6 + [ 5] 0x00007f27db606000 /lib64/ld-linux-x86-64.so.2 + """ + target = self.dbg.CreateTarget(None) + process = target.LoadCore("linux-x86_64-no-exe.core") + self.assertTrue(process, PROCESS_IS_VALID) + num_modules = target.GetNumModules() + self.assertEqual(num_modules, 6) + + m = target.module["/data/users/gclayton/args/elf-crash"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x000055bb04288000) + self.assertEqual(m.GetUUIDString(), "7BCC1101") + + m = target.module["/lib64/libstdc++.so.6"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x00007f27db200000) + self.assertEqual(m.GetUUIDString(), None) + + m = target.module["/lib64/libm.so.6"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x00007f27db51b000) + self.assertEqual(m.GetUUIDString(), + "AF275675-4671-8B49-24C8-A9A657D74115-C80DEE65") + + m = target.module["/lib64/libgcc_s.so.1"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x00007f27db4fe000) + self.assertEqual(m.GetUUIDString(), None) + + m = target.module["/lib64/libc.so.6"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x00007f27dae00000) + self.assertEqual(m.GetUUIDString(), None) + + m = target.module["/lib64/ld-linux-x86-64.so.2"] + self.assertTrue(m.IsValid()) + self.assertEqual(m.GetObjectFileHeaderAddress().GetLoadAddress(target), + 0x00007f27db606000) + self.assertEqual(m.GetUUIDString(), None) + + self.dbg.DeleteTarget(target) + def check_memory_regions(self, process, region_count): region_list = process.GetMemoryRegions() self.assertEqual(region_list.GetSize(), region_count) diff --git a/lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core b/lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core new file mode 100644 index 0000000000000..74e84b0631033 Binary files /dev/null and b/lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core differ `````````` </details> https://github.com/llvm/llvm-project/pull/177289 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
