[Lldb-commits] [lldb] Read and store gnu build id from loaded core file (PR #92492)

via lldb-commits lldb-commits at lists.llvm.org
Wed May 22 08:45:53 PDT 2024


https://github.com/GeorgeHuyubo updated https://github.com/llvm/llvm-project/pull/92492

>From fc7ae3cd19a999375504733be6c942978d80d5d7 Mon Sep 17 00:00:00 2001
From: George Hu <huyubohyb at gmail.com>
Date: Tue, 14 May 2024 16:18:20 -0700
Subject: [PATCH] Read and store gnu build id from loaded core file

---
 .../Plugins/ObjectFile/ELF/ELFHeader.cpp      | 13 +++
 .../source/Plugins/ObjectFile/ELF/ELFHeader.h | 25 ++++++
 .../Process/elf-core/ProcessElfCore.cpp       | 87 +++++++++++++++++++
 .../Plugins/Process/elf-core/ProcessElfCore.h | 15 ++++
 4 files changed, 140 insertions(+)

diff --git a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
index a6e385f70709b..fcb677ed28a92 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp
@@ -252,6 +252,19 @@ bool ELFSectionHeader::Parse(const lldb_private::DataExtractor &data,
   return true;
 }
 
+// SectionNote
+
+SectionNote::SectionNote() { memset(this, 0, sizeof(SectionNote)); }
+
+bool SectionNote::Parse(const lldb_private::DataExtractor &data,
+                        lldb::offset_t *offset) {
+  // Read sn_namesz and sn_descsz, sn_type.
+  if (data.GetU32(offset, &sn_namesz, 3) == nullptr)
+    return false;
+
+  return true;
+}
+
 // ELFSymbol
 
 ELFSymbol::ELFSymbol() { memset(this, 0, sizeof(ELFSymbol)); }
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
index 963cc850736ff..da6dfc7343bcf 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
+++ b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h
@@ -271,6 +271,31 @@ struct ELFSymbol {
             const lldb_private::SectionList *section_list);
 };
 
+/// \class SectionNote
+/// Represents an entry of PT_NOTE in program header
+struct SectionNote {
+  elf_word sn_namesz;
+  elf_word sn_descsz;
+  elf_word sn_type;
+
+  SectionNote();
+
+  /// Parse an SectionNote entry from the given DataExtractor starting at
+  /// position \p offset.
+  ///
+  /// \param[in] data
+  ///    The DataExtractor to read from.
+  ///
+  /// \param[in,out] offset
+  ///    Pointer to an offset in the data.  On return the offset will be
+  ///    advanced by the number of bytes read.
+  ///
+  /// \return
+  ///    True if the SectionNote was successfully read and false
+  ///    otherwise.
+  bool Parse(const lldb_private::DataExtractor &data, lldb::offset_t *offset);
+};
+
 /// \class ELFDynamic
 /// Represents an entry in an ELF dynamic table.
 struct ELFDynamic {
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 36812c27a5b6d..4e5e70ceae438 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -6,11 +6,14 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include <cstddef>
 #include <cstdlib>
 
 #include <memory>
 #include <mutex>
+#include <tuple>
 
+#include "Plugins/ObjectFile/ELF/ELFHeader.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/PluginManager.h"
@@ -33,6 +36,7 @@
 #include "Plugins/Process/elf-core/RegisterUtilities.h"
 #include "ProcessElfCore.h"
 #include "ThreadElfCore.h"
+#include "lldb/lldb-types.h"
 
 using namespace lldb_private;
 namespace ELF = llvm::ELF;
@@ -250,6 +254,9 @@ Status ProcessElfCore::DoLoadCore() {
     }
   }
 
+  // Try to find gnu build id before we load the executable.
+  UpdateBuildIdForNTFileEntries();
+
   // Core files are useless without the main executable. See if we can locate
   // the main executable using data we found in the core file notes.
   lldb::ModuleSP exe_module_sp = GetTarget().GetExecutableModule();
@@ -258,6 +265,7 @@ Status ProcessElfCore::DoLoadCore() {
     if (!m_nt_file_entries.empty()) {
       ModuleSpec exe_module_spec;
       exe_module_spec.GetArchitecture() = arch;
+      exe_module_spec.GetUUID() = m_nt_file_entries[0].uuid;
       exe_module_spec.GetFileSpec().SetFile(m_nt_file_entries[0].path,
                                             FileSpec::Style::native);
       if (exe_module_spec.GetFileSpec()) {
@@ -271,6 +279,17 @@ Status ProcessElfCore::DoLoadCore() {
   return error;
 }
 
+void ProcessElfCore::UpdateBuildIdForNTFileEntries() {
+  if (!m_nt_file_entries.empty()) {
+    for (NT_FILE_Entry &entry : m_nt_file_entries) {
+      std::optional<UUID> uuid =
+          FindNoteInCoreMemory(entry.start, llvm::ELF::NT_GNU_BUILD_ID);
+      if (uuid)
+        entry.uuid = uuid.value();
+    }
+  }
+}
+
 lldb_private::DynamicLoader *ProcessElfCore::GetDynamicLoader() {
   if (m_dyld_up.get() == nullptr)
     m_dyld_up.reset(DynamicLoader::FindPlugin(
@@ -983,6 +1002,74 @@ llvm::Error ProcessElfCore::ParseThreadContextsFromNoteSegment(
   }
 }
 
+bool ProcessElfCore::IsElf(lldb::addr_t address) {
+  uint8_t buf[4];
+  Status error;
+  size_t byte_read = ReadMemory(address, buf, 4, error);
+  if (byte_read != 4)
+    return false;
+  return elf::ELFHeader::MagicBytesMatch(buf);
+}
+
+std::optional<UUID> ProcessElfCore::FindNoteInCoreMemory(lldb::addr_t address,
+                                                         uint32_t type) {
+  if (!IsElf(address))
+    return std::nullopt;
+  const uint32_t addr_size = GetAddressByteSize();
+  const size_t elf_header_size = addr_size == 4 ? sizeof(llvm::ELF::Elf32_Ehdr)
+                                                : sizeof(llvm::ELF::Elf64_Ehdr);
+
+  unsigned char buf[4096];
+  Status error;
+  size_t byte_read = ReadMemory(address, buf, elf_header_size, error);
+  if (byte_read != elf_header_size)
+    return std::nullopt;
+  DataExtractor data(buf, 4096, GetByteOrder(), addr_size);
+  lldb::offset_t offset = 0;
+
+  elf::ELFHeader elf_header;
+  elf_header.Parse(data, &offset);
+
+  const lldb::addr_t ph_addr = address + elf_header.e_phoff;
+
+  for (unsigned int i = 0; i < elf_header.e_phnum; ++i) {
+    byte_read = ReadMemory(ph_addr + i * elf_header.e_phentsize, buf,
+                           elf_header.e_phentsize, error);
+    if (byte_read != elf_header.e_phentsize)
+      break;
+    offset = 0;
+    elf::ELFProgramHeader program_header;
+    program_header.Parse(data, &offset);
+    if (program_header.p_type != llvm::ELF::PT_NOTE)
+      continue;
+
+    byte_read =
+        ReadMemory(program_header.p_vaddr, buf, program_header.p_memsz, error);
+    if (byte_read != program_header.p_memsz)
+      continue;
+    return parseSegment(data, program_header.p_memsz, type);
+  }
+  return std::nullopt;
+}
+
+std::optional<UUID> parseSegment(const DataExtractor &segment_data,
+                                 unsigned long memsz, uint32_t type) {
+  elf::SectionNote note;
+  lldb::offset_t offset = 0;
+  while (offset < memsz) {
+    note.Parse(segment_data, &offset);
+    if (note.sn_namesz == 4 && note.sn_type == type) {
+      const char *name = segment_data.GetCStr(&offset);
+      if (name && strcmp("GNU", name) == 0)
+        return UUID(
+            llvm::ArrayRef<uint8_t>(segment_data.GetDataStart() + offset,
+                                    note.sn_descsz /*byte size*/));
+    }
+    offset += note.sn_namesz + note.sn_descsz;
+  }
+  return std::nullopt;
+}
+
 uint32_t ProcessElfCore::GetNumThreadContexts() {
   if (!m_thread_data_valid)
     DoLoadCore();
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
index 2cec635bbacfe..740465dce928c 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -117,6 +117,7 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
     lldb::addr_t end;
     lldb::addr_t file_ofs;
     std::string path;
+    lldb_private::UUID uuid; //.note.gnu.build-id
   };
 
   // For ProcessElfCore only
@@ -158,6 +159,20 @@ class ProcessElfCore : public lldb_private::PostMortemProcess {
   // Returns number of thread contexts stored in the core file
   uint32_t GetNumThreadContexts();
 
+  // Populate gnu uuid for each NT_FILE entry
+  void UpdateBuildIdForNTFileEntries();
+
+  // Returns the value of certain type of note of a given start address
+  std::optional<lldb_private::UUID> FindNoteInCoreMemory(lldb::addr_t address,
+                                                         uint32_t type);
+
+  // Returns true if the given address is a start of ELF file
+  bool IsElf(lldb::addr_t address);
+
+  std::optional<lldb_private::UUID>
+  parseSegment(const lldb_private::DataExtractor &segment_data,
+               unsigned long memsz, uint32_t type);
+
   // Parse a contiguous address range of the process from LOAD segment
   lldb::addr_t
   AddAddressRangeFromLoadSegment(const elf::ELFProgramHeader &header);



More information about the lldb-commits mailing list