[Lldb-commits] [lldb] 28ba8a5 - [LLDB] Impove ObjectFileELF's .dynamic parsing and usage. (#101237)

via lldb-commits lldb-commits at lists.llvm.org
Thu Aug 8 11:04:57 PDT 2024


Author: Greg Clayton
Date: 2024-08-08T11:04:52-07:00
New Revision: 28ba8a56b6fb9ec61897fa84369f46e43be94c03

URL: https://github.com/llvm/llvm-project/commit/28ba8a56b6fb9ec61897fa84369f46e43be94c03
DIFF: https://github.com/llvm/llvm-project/commit/28ba8a56b6fb9ec61897fa84369f46e43be94c03.diff

LOG: [LLDB] Impove ObjectFileELF's .dynamic parsing and usage. (#101237)

This patch improves the ability of a ObjectFileELF instance to read the .dynamic section. It adds the ability to read the .dynamic section from the PT_DYNAMIC program header which is useful for ELF files that have no section headers and for ELF files that are read from memory. It cleans up the usage of the .dynamic entries so that ObjectFileELF::ParseDynamicSymbols() is the only code that parses .dynamic entries, teaches that function the read and store the string values for each .dynamic entry. We now dump the .dynamic entries in the output of "image dump objfile". It also cleans up the code that gets the dynamic string table so that it can grab it from the DT_STRTAB and DT_STRSZ .dynamic entries for when we have a ELF file with no section headers or we are reading it from memory.

Added: 
    lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp
    lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml
    lldb/test/Shell/ObjectFile/ELF/elf-memory.test

Modified: 
    lldb/include/lldb/Symbol/ObjectFile.h
    lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
    lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
    lldb/source/Symbol/ObjectFile.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index 8592323322e383..d89314d44bf671 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -656,8 +656,9 @@ class ObjectFile : public std::enable_shared_from_this<ObjectFile>,
   // When an object file is in memory, subclasses should try and lock the
   // process weak pointer. If the process weak pointer produces a valid
   // ProcessSP, then subclasses can call this function to read memory.
-  static lldb::DataBufferSP ReadMemory(const lldb::ProcessSP &process_sp,
-                                       lldb::addr_t addr, size_t byte_size);
+  static lldb::WritableDataBufferSP
+  ReadMemory(const lldb::ProcessSP &process_sp, lldb::addr_t addr,
+             size_t byte_size);
 
   // This function returns raw file contents. Do not use it if you want
   // transparent decompression of section contents.

diff  --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 890db5c2748146..414ac6112d5797 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -419,19 +419,35 @@ ObjectFile *ObjectFileELF::CreateInstance(const lldb::ModuleSP &module_sp,
 ObjectFile *ObjectFileELF::CreateMemoryInstance(
     const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp,
     const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
-  if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) {
-    const uint8_t *magic = data_sp->GetBytes();
-    if (ELFHeader::MagicBytesMatch(magic)) {
-      unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
-      if (address_size == 4 || address_size == 8) {
-        std::unique_ptr<ObjectFileELF> objfile_up(
-            new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
-        ArchSpec spec = objfile_up->GetArchitecture();
-        if (spec && objfile_up->SetModulesArchitecture(spec))
-          return objfile_up.release();
-      }
-    }
-  }
+  if (!data_sp || data_sp->GetByteSize() < (llvm::ELF::EI_NIDENT))
+    return nullptr;
+  const uint8_t *magic = data_sp->GetBytes();
+  if (!ELFHeader::MagicBytesMatch(magic))
+    return nullptr;
+  // Read the ELF header first so we can figure out how many bytes we need
+  // to read to get as least the ELF header + program headers.
+  DataExtractor data;
+  data.SetData(data_sp);
+  elf::ELFHeader hdr;
+  lldb::offset_t offset = 0;
+  if (!hdr.Parse(data, &offset))
+    return nullptr;
+
+  // Make sure the address size is set correctly in the ELF header.
+  if (!hdr.Is32Bit() && !hdr.Is64Bit())
+    return nullptr;
+  // Figure out where the program headers end and read enough bytes to get the
+  // program headers in their entirety.
+  lldb::offset_t end_phdrs = hdr.e_phoff + (hdr.e_phentsize * hdr.e_phnum);
+  if (end_phdrs > data_sp->GetByteSize())
+    data_sp = ReadMemory(process_sp, header_addr, end_phdrs);
+
+  std::unique_ptr<ObjectFileELF> objfile_up(
+      new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
+  ArchSpec spec = objfile_up->GetArchitecture();
+  if (spec && objfile_up->SetModulesArchitecture(spec))
+    return objfile_up.release();
+
   return nullptr;
 }
 
@@ -873,42 +889,37 @@ Address ObjectFileELF::GetImageInfoAddress(Target *target) {
   if (!section_list)
     return Address();
 
-  // Find the SHT_DYNAMIC (.dynamic) section.
-  SectionSP dynsym_section_sp(
-      section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true));
-  if (!dynsym_section_sp)
-    return Address();
-  assert(dynsym_section_sp->GetObjectFile() == this);
+  for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
+    const ELFDynamic &symbol = m_dynamic_symbols[i].symbol;
 
-  user_id_t dynsym_id = dynsym_section_sp->GetID();
-  const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id);
-  if (!dynsym_hdr)
-    return Address();
+    if (symbol.d_tag != DT_DEBUG && symbol.d_tag != DT_MIPS_RLD_MAP &&
+        symbol.d_tag != DT_MIPS_RLD_MAP_REL)
+      continue;
 
-  for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
-    ELFDynamic &symbol = m_dynamic_symbols[i];
+    // Compute the offset as the number of previous entries plus the size of
+    // d_tag.
+    const addr_t offset = (i * 2 + 1) * GetAddressByteSize();
+    const addr_t d_file_addr = m_dynamic_base_addr + offset;
+    Address d_addr;
+    if (d_addr.ResolveAddressUsingFileSections(d_file_addr, GetSectionList()))
+      return Address();
+    if (symbol.d_tag == DT_DEBUG)
+      return d_addr;
 
-    if (symbol.d_tag == DT_DEBUG) {
-      // Compute the offset as the number of previous entries plus the size of
-      // d_tag.
-      addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
-      return Address(dynsym_section_sp, offset);
-    }
     // MIPS executables uses DT_MIPS_RLD_MAP_REL to support PIE. DT_MIPS_RLD_MAP
     // exists in non-PIE.
-    else if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
-              symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
-             target) {
-      addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
-      addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target);
-      if (dyn_base == LLDB_INVALID_ADDRESS)
+    if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
+         symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
+        target) {
+      const addr_t d_load_addr = d_addr.GetLoadAddress(target);
+      if (d_load_addr == LLDB_INVALID_ADDRESS)
         return Address();
 
       Status error;
       if (symbol.d_tag == DT_MIPS_RLD_MAP) {
         // DT_MIPS_RLD_MAP tag stores an absolute address of the debug pointer.
         Address addr;
-        if (target->ReadPointerFromMemory(dyn_base + offset, error, addr, true))
+        if (target->ReadPointerFromMemory(d_load_addr, error, addr, true))
           return addr;
       }
       if (symbol.d_tag == DT_MIPS_RLD_MAP_REL) {
@@ -916,18 +927,17 @@ Address ObjectFileELF::GetImageInfoAddress(Target *target) {
         // relative to the address of the tag.
         uint64_t rel_offset;
         rel_offset = target->ReadUnsignedIntegerFromMemory(
-            dyn_base + offset, GetAddressByteSize(), UINT64_MAX, error, true);
+            d_load_addr, GetAddressByteSize(), UINT64_MAX, error, true);
         if (error.Success() && rel_offset != UINT64_MAX) {
           Address addr;
           addr_t debug_ptr_address =
-              dyn_base + (offset - GetAddressByteSize()) + rel_offset;
+              d_load_addr - GetAddressByteSize() + rel_offset;
           addr.SetOffset(debug_ptr_address);
           return addr;
         }
       }
     }
   }
-
   return Address();
 }
 
@@ -970,62 +980,23 @@ Address ObjectFileELF::GetBaseAddress() {
   return LLDB_INVALID_ADDRESS;
 }
 
-// ParseDependentModules
 size_t ObjectFileELF::ParseDependentModules() {
   if (m_filespec_up)
     return m_filespec_up->GetSize();
 
   m_filespec_up = std::make_unique<FileSpecList>();
 
-  if (!ParseSectionHeaders())
-    return 0;
-
-  SectionList *section_list = GetSectionList();
-  if (!section_list)
-    return 0;
-
-  // Find the SHT_DYNAMIC section.
-  Section *dynsym =
-      section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
-          .get();
-  if (!dynsym)
-    return 0;
-  assert(dynsym->GetObjectFile() == this);
-
-  const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex(dynsym->GetID());
-  if (!header)
-    return 0;
-  // sh_link: section header index of string table used by entries in the
-  // section.
-  Section *dynstr = section_list->FindSectionByID(header->sh_link).get();
-  if (!dynstr)
-    return 0;
-
-  DataExtractor dynsym_data;
-  DataExtractor dynstr_data;
-  if (ReadSectionData(dynsym, dynsym_data) &&
-      ReadSectionData(dynstr, dynstr_data)) {
-    ELFDynamic symbol;
-    const lldb::offset_t section_size = dynsym_data.GetByteSize();
-    lldb::offset_t offset = 0;
-
-    // The only type of entries we are concerned with are tagged DT_NEEDED,
-    // yielding the name of a required library.
-    while (offset < section_size) {
-      if (!symbol.Parse(dynsym_data, &offset))
-        break;
-
-      if (symbol.d_tag != DT_NEEDED)
+  if (ParseDynamicSymbols()) {
+    for (const auto &entry : m_dynamic_symbols) {
+      if (entry.symbol.d_tag != DT_NEEDED)
         continue;
-
-      uint32_t str_index = static_cast<uint32_t>(symbol.d_val);
-      const char *lib_name = dynstr_data.PeekCStr(str_index);
-      FileSpec file_spec(lib_name);
-      FileSystem::Instance().Resolve(file_spec);
-      m_filespec_up->Append(file_spec);
+      if (!entry.name.empty()) {
+        FileSpec file_spec(entry.name);
+        FileSystem::Instance().Resolve(file_spec);
+        m_filespec_up->Append(file_spec);
+      }
     }
   }
-
   return m_filespec_up->GetSize();
 }
 
@@ -2472,48 +2443,47 @@ size_t ObjectFileELF::ParseDynamicSymbols() {
   if (m_dynamic_symbols.size())
     return m_dynamic_symbols.size();
 
-  SectionList *section_list = GetSectionList();
-  if (!section_list)
+  std::optional<DataExtractor> dynamic_data = GetDynamicData();
+  if (!dynamic_data)
     return 0;
 
-  // Find the SHT_DYNAMIC section.
-  Section *dynsym =
-      section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
-          .get();
-  if (!dynsym)
-    return 0;
-  assert(dynsym->GetObjectFile() == this);
-
-  ELFDynamic symbol;
-  DataExtractor dynsym_data;
-  if (ReadSectionData(dynsym, dynsym_data)) {
-    const lldb::offset_t section_size = dynsym_data.GetByteSize();
-    lldb::offset_t cursor = 0;
-
-    while (cursor < section_size) {
-      if (!symbol.Parse(dynsym_data, &cursor))
+  ELFDynamicWithName e;
+  lldb::offset_t cursor = 0;
+  while (e.symbol.Parse(*dynamic_data, &cursor)) {
+    m_dynamic_symbols.push_back(e);
+    if (e.symbol.d_tag == DT_NULL)
+      break;
+  }
+  if (std::optional<DataExtractor> dynstr_data = GetDynstrData()) {
+    for (ELFDynamicWithName &entry : m_dynamic_symbols) {
+      switch (entry.symbol.d_tag) {
+      case DT_NEEDED:
+      case DT_SONAME:
+      case DT_RPATH:
+      case DT_RUNPATH:
+      case DT_AUXILIARY:
+      case DT_FILTER: {
+        lldb::offset_t cursor = entry.symbol.d_val;
+        const char *name = dynstr_data->GetCStr(&cursor);
+        if (name)
+          entry.name = std::string(name);
         break;
-
-      m_dynamic_symbols.push_back(symbol);
+      }
+      default:
+        break;
+      }
     }
   }
-
   return m_dynamic_symbols.size();
 }
 
 const ELFDynamic *ObjectFileELF::FindDynamicSymbol(unsigned tag) {
   if (!ParseDynamicSymbols())
     return nullptr;
-
-  DynamicSymbolCollIter I = m_dynamic_symbols.begin();
-  DynamicSymbolCollIter E = m_dynamic_symbols.end();
-  for (; I != E; ++I) {
-    ELFDynamic *symbol = &*I;
-
-    if (symbol->d_tag == tag)
-      return symbol;
+  for (const auto &entry : m_dynamic_symbols) {
+    if (entry.symbol.d_tag == tag)
+      return &entry.symbol;
   }
-
   return nullptr;
 }
 
@@ -3230,7 +3200,10 @@ void ObjectFileELF::Dump(Stream *s) {
   ArchSpec header_arch = GetArchitecture();
 
   *s << ", file = '" << m_file
-     << "', arch = " << header_arch.GetArchitectureName() << "\n";
+     << "', arch = " << header_arch.GetArchitectureName();
+  if (m_memory_addr != LLDB_INVALID_ADDRESS)
+    s->Printf(", addr = %#16.16" PRIx64, m_memory_addr);
+  s->EOL();
 
   DumpELFHeader(s, m_header);
   s->EOL();
@@ -3248,6 +3221,8 @@ void ObjectFileELF::Dump(Stream *s) {
   s->EOL();
   DumpDependentModules(s);
   s->EOL();
+  DumpELFDynamic(s);
+  s->EOL();
 }
 
 // DumpELFHeader
@@ -3492,6 +3467,111 @@ void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) {
   }
 }
 
+std::string static getDynamicTagAsString(uint16_t Arch, uint64_t Type) {
+#define DYNAMIC_STRINGIFY_ENUM(tag, value)                                     \
+  case value:                                                                  \
+    return #tag;
+
+#define DYNAMIC_TAG(n, v)
+  switch (Arch) {
+  case llvm::ELF::EM_AARCH64:
+    switch (Type) {
+#define AARCH64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef AARCH64_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_HEXAGON:
+    switch (Type) {
+#define HEXAGON_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef HEXAGON_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_MIPS:
+    switch (Type) {
+#define MIPS_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef MIPS_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_PPC:
+    switch (Type) {
+#define PPC_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef PPC_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_PPC64:
+    switch (Type) {
+#define PPC64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef PPC64_DYNAMIC_TAG
+    }
+    break;
+
+  case llvm::ELF::EM_RISCV:
+    switch (Type) {
+#define RISCV_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef RISCV_DYNAMIC_TAG
+    }
+    break;
+  }
+#undef DYNAMIC_TAG
+  switch (Type) {
+// Now handle all dynamic tags except the architecture specific ones
+#define AARCH64_DYNAMIC_TAG(name, value)
+#define MIPS_DYNAMIC_TAG(name, value)
+#define HEXAGON_DYNAMIC_TAG(name, value)
+#define PPC_DYNAMIC_TAG(name, value)
+#define PPC64_DYNAMIC_TAG(name, value)
+#define RISCV_DYNAMIC_TAG(name, value)
+// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc.
+#define DYNAMIC_TAG_MARKER(name, value)
+#define DYNAMIC_TAG(name, value)                                               \
+  case value:                                                                  \
+    return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef DYNAMIC_TAG
+#undef AARCH64_DYNAMIC_TAG
+#undef MIPS_DYNAMIC_TAG
+#undef HEXAGON_DYNAMIC_TAG
+#undef PPC_DYNAMIC_TAG
+#undef PPC64_DYNAMIC_TAG
+#undef RISCV_DYNAMIC_TAG
+#undef DYNAMIC_TAG_MARKER
+#undef DYNAMIC_STRINGIFY_ENUM
+  default:
+    return "<unknown:>0x" + llvm::utohexstr(Type, true);
+  }
+}
+
+void ObjectFileELF::DumpELFDynamic(lldb_private::Stream *s) {
+  ParseDynamicSymbols();
+  if (m_dynamic_symbols.empty())
+    return;
+
+  s->PutCString(".dynamic:\n");
+  s->PutCString("IDX  d_tag            d_val/d_ptr\n");
+  s->PutCString("==== ---------------- ------------------\n");
+  uint32_t idx = 0;
+  for (const auto &entry : m_dynamic_symbols) {
+    s->Printf("[%2u] ", idx++);
+    s->Printf(
+        "%-16s 0x%16.16" PRIx64,
+        getDynamicTagAsString(m_header.e_machine, entry.symbol.d_tag).c_str(),
+        entry.symbol.d_ptr);
+    if (!entry.name.empty())
+      s->Printf(" \"%s\"", entry.name.c_str());
+    s->EOL();
+  }
+}
+
 ArchSpec ObjectFileELF::GetArchitecture() {
   if (!ParseHeader())
     return ArchSpec();
@@ -3664,7 +3744,24 @@ llvm::ArrayRef<ELFProgramHeader> ObjectFileELF::ProgramHeaders() {
 }
 
 DataExtractor ObjectFileELF::GetSegmentData(const ELFProgramHeader &H) {
-  return DataExtractor(m_data, H.p_offset, H.p_filesz);
+  // Try and read the program header from our cached m_data which can come from
+  // the file on disk being mmap'ed or from the initial part of the ELF file we
+  // read from memory and cached.
+  DataExtractor data = DataExtractor(m_data, H.p_offset, H.p_filesz);
+  if (data.GetByteSize() == H.p_filesz)
+    return data;
+  if (IsInMemory()) {
+    // We have a ELF file in process memory, read the program header data from
+    // the process.
+    if (ProcessSP process_sp = m_process_wp.lock()) {
+      const lldb::offset_t base_file_addr = GetBaseAddress().GetFileAddress();
+      const addr_t load_bias = m_memory_addr - base_file_addr;
+      const addr_t data_addr = H.p_vaddr + load_bias;
+      if (DataBufferSP data_sp = ReadMemory(process_sp, data_addr, H.p_memsz))
+        return DataExtractor(data_sp, GetByteOrder(), GetAddressByteSize());
+    }
+  }
+  return DataExtractor();
 }
 
 bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
@@ -3704,3 +3801,88 @@ ObjectFileELF::MapFileDataWritable(const FileSpec &file, uint64_t Size,
   return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size,
                                                          Offset);
 }
+
+std::optional<DataExtractor> ObjectFileELF::GetDynstrData() {
+  if (SectionList *section_list = GetSectionList()) {
+    // Find the SHT_DYNAMIC section.
+    if (Section *dynamic =
+            section_list
+                ->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+                .get()) {
+      assert(dynamic->GetObjectFile() == this);
+      if (const ELFSectionHeaderInfo *header =
+              GetSectionHeaderByIndex(dynamic->GetID())) {
+        // sh_link: section header index of string table used by entries in
+        // the section.
+        if (Section *dynstr =
+                section_list->FindSectionByID(header->sh_link).get()) {
+          DataExtractor data;
+          if (ReadSectionData(dynstr, data))
+            return data;
+        }
+      }
+    }
+  }
+
+  // Every ELF file which represents an executable or shared library has
+  // mandatory .dynamic entries. Two of these values are DT_STRTAB and DT_STRSZ
+  // and represent the dynamic symbol tables's string table. These are needed
+  // by the dynamic loader and we can read them from a process' address space.
+  //
+  // When loading and ELF file from memory, only the program headers end up
+  // being mapped into memory, and we can find these values in the PT_DYNAMIC
+  // segment.
+  const ELFDynamic *strtab = FindDynamicSymbol(DT_STRTAB);
+  const ELFDynamic *strsz = FindDynamicSymbol(DT_STRSZ);
+  if (strtab == nullptr || strsz == nullptr)
+    return std::nullopt;
+
+  if (ProcessSP process_sp = m_process_wp.lock()) {
+    if (DataBufferSP data_sp =
+            ReadMemory(process_sp, strtab->d_ptr, strsz->d_val))
+      return DataExtractor(data_sp, GetByteOrder(), GetAddressByteSize());
+  } else {
+    // We have an ELF file with no section headers or we didn't find the
+    // .dynamic section. Try and find the .dynstr section.
+    Address addr;
+    if (addr.ResolveAddressUsingFileSections(strtab->d_ptr, GetSectionList())) {
+      DataExtractor data;
+      addr.GetSection()->GetSectionData(data);
+      return DataExtractor(data,
+                           strtab->d_ptr - addr.GetSection()->GetFileAddress(),
+                           strsz->d_val);
+    }
+  }
+  return std::nullopt;
+}
+
+std::optional<lldb_private::DataExtractor> ObjectFileELF::GetDynamicData() {
+  DataExtractor data;
+  // The PT_DYNAMIC program header describes where the .dynamic section is and
+  // doesn't require parsing section headers. The PT_DYNAMIC is required by
+  // executables and shared libraries so it will always be available.
+  for (const ELFProgramHeader &H : ProgramHeaders()) {
+    if (H.p_type == llvm::ELF::PT_DYNAMIC) {
+      data = GetSegmentData(H);
+      if (data.GetByteSize() > 0) {
+        m_dynamic_base_addr = H.p_vaddr;
+        return data;
+      }
+    }
+  }
+  // Fall back to using section headers.
+  if (SectionList *section_list = GetSectionList()) {
+    // Find the SHT_DYNAMIC section.
+    if (Section *dynamic =
+            section_list
+                ->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+                .get()) {
+      assert(dynamic->GetObjectFile() == this);
+      if (ReadSectionData(dynamic, data)) {
+        m_dynamic_base_addr = dynamic->GetFileAddress();
+        return data;
+      }
+    }
+  }
+  return std::nullopt;
+}

diff  --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
index 844e981b1d890a..aba3a5bfcbf5b6 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -183,7 +183,11 @@ class ObjectFileELF : public lldb_private::ObjectFile {
   typedef SectionHeaderColl::iterator SectionHeaderCollIter;
   typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter;
 
-  typedef std::vector<elf::ELFDynamic> DynamicSymbolColl;
+  struct ELFDynamicWithName {
+    elf::ELFDynamic symbol;
+    std::string name;
+  };
+  typedef std::vector<ELFDynamicWithName> DynamicSymbolColl;
   typedef DynamicSymbolColl::iterator DynamicSymbolCollIter;
   typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter;
 
@@ -213,6 +217,10 @@ class ObjectFileELF : public lldb_private::ObjectFile {
   /// Collection of section headers.
   SectionHeaderColl m_section_headers;
 
+  /// The file address of the .dynamic section. This can be found in the p_vaddr
+  /// of the PT_DYNAMIC program header.
+  lldb::addr_t m_dynamic_base_addr = LLDB_INVALID_ADDRESS;
+
   /// Collection of symbols from the dynamic table.
   DynamicSymbolColl m_dynamic_symbols;
 
@@ -384,6 +392,9 @@ class ObjectFileELF : public lldb_private::ObjectFile {
   /// ELF dependent module dump routine.
   void DumpDependentModules(lldb_private::Stream *s);
 
+  /// ELF dump the .dynamic section
+  void DumpELFDynamic(lldb_private::Stream *s);
+
   const elf::ELFDynamic *FindDynamicSymbol(unsigned tag);
 
   unsigned PLTRelocationType();
@@ -402,6 +413,28 @@ class ObjectFileELF : public lldb_private::ObjectFile {
   /// .gnu_debugdata section or \c nullptr if an error occured or if there's no
   /// section with that name.
   std::shared_ptr<ObjectFileELF> GetGnuDebugDataObjectFile();
+
+  /// Get the bytes that represent the .dynamic section.
+  ///
+  /// This function will fetch the data for the .dynamic section in an ELF file.
+  /// The PT_DYNAMIC program header will be used to extract the data and this
+  /// function will fall back to using the section headers if PT_DYNAMIC isn't
+  /// found.
+  ///
+  /// \return The bytes that represent the string table data or \c std::nullopt
+  ///         if an error occured.
+  std::optional<lldb_private::DataExtractor> GetDynamicData();
+
+  /// Get the bytes that represent the dynamic string table data.
+  ///
+  /// This function will fetch the data for the string table in an ELF file. If
+  /// the ELF file is loaded from a file on disk, it will use the section
+  /// headers to extract the data and fall back to using the DT_STRTAB and
+  /// DT_STRSZ .dynamic entries.
+  ///
+  /// \return The bytes that represent the string table data or \c std::nullopt
+  ///         if an error occured.
+  std::optional<lldb_private::DataExtractor> GetDynstrData();
 };
 
 #endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H

diff  --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp
index 2608a9c5fb79a2..35317d209de1f9 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -454,9 +454,10 @@ AddressClass ObjectFile::GetAddressClass(addr_t file_addr) {
   return AddressClass::eUnknown;
 }
 
-DataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp,
-                                    lldb::addr_t addr, size_t byte_size) {
-  DataBufferSP data_sp;
+WritableDataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp,
+                                            lldb::addr_t addr,
+                                            size_t byte_size) {
+  WritableDataBufferSP data_sp;
   if (process_sp) {
     std::unique_ptr<DataBufferHeap> data_up(new DataBufferHeap(byte_size, 0));
     Status error;

diff  --git a/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp b/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp
new file mode 100644
index 00000000000000..9cae6c99c9f761
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp
@@ -0,0 +1,5 @@
+#include <stdio.h>
+int main() {
+  printf("Hello World\n"); // Use something from libc.so
+  return 0;
+}

diff  --git a/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml b/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml
new file mode 100644
index 00000000000000..bc34e16e0b6ad2
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml
@@ -0,0 +1,90 @@
+## This test verifies that loading an ELF file that has no section headers can
+## find the contents on the .dynamic section and the strings associated with
+## the .dynamic seciton.
+## - Loading the .dynamic section from the PT_DYNAMIC
+## This test will make a simple executable that links against libc.so and we
+## verify that we can find the DT_NEEDED entry with the shared library found
+## in the .dynamic dump from "image dump objfile"
+
+# RUN: yaml2obj %s -o %t
+
+# RUN: %lldb -b \
+# RUN:   -o "target create -d '%t'" \
+# RUN:   -o "image dump objfile" \
+# RUN:   | FileCheck %s --dump-input=always
+# CHECK: (lldb) image dump objfile
+# CHECK: Dumping headers for 1 module(s).
+# CHECK: ObjectFileELF, file =
+# CHECK: ELF Header
+# Make sure there are no section headers
+# CHECK: e_shnum = 0x00000000
+
+# Make sure we find the program headers and see a PT_DYNAMIC entry.
+# CHECK: Program Headers
+# CHECK: IDX  p_type          p_offset p_vaddr  p_paddr  p_filesz p_memsz  p_flags                   p_align
+# CHECK: ==== --------------- -------- -------- -------- -------- -------- ------------------------- --------
+# CHECK: [ 0] PT_LOAD         000000b0 00000000 00000000 00000170 00000170 00000000 (              ) 00000001
+# CHECK: [ 1] PT_DYNAMIC      000001b0 00000100 00000100 00000070 00000070 00000000 (              ) 00000008
+
+# CHECK: Dependent Modules:
+# CHECK:    ccc
+# CHECK:    aaa
+# CHECK:    bbb
+
+# Make sure we see some sections created from the program headers
+# MAIN: SectID
+# MAIN: PT_LOAD[0]
+
+# CHECK: .dynamic:
+# CHECK: IDX  d_tag            d_val/d_ptr
+# CHECK: ==== ---------------- ------------------
+# CHECK: [ 0] STRTAB           0x0000000000000000
+# CHECK: [ 1] NEEDED           0x0000000000000009 "ccc"
+# CHECK: [ 2] NEEDED           0x0000000000000001 "aaa"
+# CHECK: [ 3] NEEDED           0x0000000000000005 "bbb"
+# CHECK: [ 4] STRSZ            0x0000000000000100
+# CHECK: [ 5] DEBUG            0x00000000deadbeef
+# CHECK: [ 6] NULL             0x0000000000000000
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Type:      SectionHeaderTable
+    NoHeaders: true
+  - Name:  .dynstr
+    Type:  SHT_STRTAB
+    Flags: [ SHF_ALLOC ]
+    Content: '00616161006262620063636300' ## 0,a,a,a,0,b,b,b,0,c,c,c,0
+    Size:    0x100
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Address: 0x100
+    Entries:
+      - Tag:   DT_STRTAB
+        Value: 0x0000000000000000
+      - Tag:   DT_NEEDED
+        Value: 9
+      - Tag:   DT_NEEDED
+        Value: 1
+      - Tag:   DT_NEEDED
+        Value: 5
+      - Tag:   DT_STRSZ
+        Value: 0x100
+      - Tag:   DT_DEBUG
+        Value: 0xdeadbeef
+      - Tag:   DT_NULL
+        Value: 0x0
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x0
+    FirstSec: .dynstr
+    LastSec:  .dynamic
+  - Type:            PT_DYNAMIC
+    FirstSec:        .dynamic
+    LastSec:         .dynamic
+    VAddr:           0x100
+    Align:           0x8

diff  --git a/lldb/test/Shell/ObjectFile/ELF/elf-memory.test b/lldb/test/Shell/ObjectFile/ELF/elf-memory.test
new file mode 100644
index 00000000000000..0b1c01486a4b43
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/elf-memory.test
@@ -0,0 +1,55 @@
+// REQUIRES: system-linux, native
+
+// This test verifies that loading an ELF file from memory works and the new
+// features that were added when loading from memory work like:
+// - Loading the .dynamic section from the PT_DYNAMIC since ELF files loaded
+//   from memory don't have the section headers available.
+// This test will make a simple executable that:
+//   - links against libc
+//   - runs and stops at a breakpoint
+//   - create a memory ELF file
+//   - verify that "image dump objfile" will dump the dynamic section of the
+//     memory elf file and find the .dynamic string table.
+
+// RUN: %clang_host %p/Inputs/memory-elf.cpp -g -O0 -o %t
+
+// RUN: %lldb %t -b \
+// RUN:   -o "b main" \
+// RUN:   -o "run" \
+// RUN:   -o "script real_module = lldb.target.module[0]" \
+// RUN:   -o "script base_addr = real_module.GetObjectFileHeaderAddress().GetLoadAddress(lldb.target)" \
+// RUN:   -o "script mem_module = lldb.SBModule(lldb.process, base_addr)" \
+// RUN:   -o "script target2 = lldb.debugger.CreateTarget('')" \
+// RUN:   -o "script target2.AddModule(mem_module)" \
+// RUN:   -o "target select 1" \
+// RUN:   -o "image dump objfile" \
+// RUN:   | FileCheck %s --check-prefix=MAIN --dump-input=always
+// MAIN: (lldb) image dump objfile
+// MAIN: Dumping headers for 1 module(s).
+// MAIN: ObjectFileELF, file = '', arch = {{.*, addr = 0x[0-9a-f]+}}
+// MAIN: ELF Header
+
+// Make sure we find the program headers and see a PT_DYNAMIC entry.
+// MAIN: Program Headers
+// MAIN: ] PT_DYNAMIC
+
+// Make sure we see some sections created from the program headers
+// MAIN: SectID
+// MAIN: PT_LOAD[0]
+
+// Ensure we find some dependent modules as won't find these if we aren't able
+// to load the .dynamic section from the PT_DYNAMIC program header.
+// MAIN: Dependent Modules:
+
+// Check for the .dynamic dump and ensure we find all dynamic entries that are
+// required to be there and needed to get the .dynstr section and the symbol
+// table, and the DT_DEBUG entry to find the list of shared libraries.
+// MAIN: .dynamic:
+// Make sure we found the .dynstr section by checking for valid strings after NEEDED
+// MAIN-DAG: NEEDED {{0x[0-9a-f]+ ".*libc.*"}}
+// MAIN-DAG: STRTAB {{0x[0-9a-f]+}}
+// MAIN-DAG: SYMTAB {{0x[0-9a-f]+}}
+// MAIN-DAG: STRSZ {{0x[0-9a-f]+}}
+// MAIN-DAG: SYMENT {{0x[0-9a-f]+}}
+// MAIN-DAG: DEBUG {{0x[0-9a-f]+}}
+// MAIN:     NULL {{0x[0-9a-f]+}}


        


More information about the lldb-commits mailing list