Author: Greg Clayton
Date: 2026-01-28T17:49:04-08:00
New Revision: 5968e29dad63d9c866675dbce9cc284fcec8a900

URL: 
https://github.com/llvm/llvm-project/commit/5968e29dad63d9c866675dbce9cc284fcec8a900
DIFF: 
https://github.com/llvm/llvm-project/commit/5968e29dad63d9c866675dbce9cc284fcec8a900.diff

LOG: [lldb] Add the ability to load ELF core file executables and shared 
libraries from memory (#177289)

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.

Added: 
    lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core

Modified: 
    lldb/source/Core/DynamicLoader.cpp
    lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
    lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
    lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
    lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py

Removed: 
    


################################################################################
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 277851b1617f1..6705ac139f0fb 100644
--- a/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
+++ b/lldb/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp
@@ -9,6 +9,7 @@
 // Main header include
 #include "DynamicLoaderPOSIXDYLD.h"
 
+#include "Plugins/ObjectFile/Placeholder/ObjectFilePlaceholder.h"
 #include "lldb/Breakpoint/BreakpointLocation.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Module.h"
@@ -698,22 +699,35 @@ 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());
+  m_process->PrefetchModuleSpecs(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()) {
+      // Create placeholder modules for any modules we couldn't load from disk
+      // or from memory.
+      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..743eae126457a 100644
--- a/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
+++ b/lldb/test/API/functionalities/postmortem/elf-core/TestLinuxCore.py
@@ -1042,6 +1042,81 @@ 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 "/libxx/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 
/libxx/libstdc++.so.6
+        [  2] AF275675-4671-8B49-24C8-A9A657D74115-C80DEE65 0x00007f27db51b000 
/libxx/libm.so.6 (0x00007f27db51b000)
+        [  3]                                      0x00007f27db4fe000 
/libxx/libgcc_s.so.1
+        [  4]                                      0x00007f27dae00000 
/libxx/libc.so.6
+        [  5]                                      0x00007f27db606000 
/libxx/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["/libxx/libstdc++.so.6"]
+        self.assertTrue(m.IsValid())
+        self.assertEqual(
+            m.GetObjectFileHeaderAddress().GetLoadAddress(target), 
0x00007F27DB200000
+        )
+        self.assertEqual(m.GetUUIDString(), None)
+
+        m = target.module["/libxx/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["/libxx/libgcc_s.so.1"]
+        self.assertTrue(m.IsValid())
+        self.assertEqual(
+            m.GetObjectFileHeaderAddress().GetLoadAddress(target), 
0x00007F27DB4FE000
+        )
+        self.assertEqual(m.GetUUIDString(), None)
+
+        m = target.module["/libxx/libc.so.6"]
+        self.assertTrue(m.IsValid())
+        self.assertEqual(
+            m.GetObjectFileHeaderAddress().GetLoadAddress(target), 
0x00007F27DAE00000
+        )
+        self.assertEqual(m.GetUUIDString(), None)
+
+        m = target.module["/libxx/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..180180aad8447
Binary files /dev/null and 
b/lldb/test/API/functionalities/postmortem/elf-core/linux-x86_64-no-exe.core 
diff er


        
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to