https://github.com/GeorgeHuyubo updated https://github.com/llvm/llvm-project/pull/92492
>From 304528acdd3590bf4d8d1a03e31fd0970ed2eaa2 Mon Sep 17 00:00:00 2001 From: George Hu <huyubo...@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 | 27 ++++++ .../Process/elf-core/ProcessElfCore.cpp | 87 +++++++++++++++++++ .../Plugins/Process/elf-core/ProcessElfCore.h | 15 ++++ 4 files changed, 142 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..baf35d4a78c18 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h @@ -271,6 +271,33 @@ 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. The address size of the DataExtractor determines if + /// a 32 or 64 bit object is to be parsed. + /// + /// \param[in] data + /// The DataExtractor to read from. The address size of the extractor + /// determines if a 32 or 64 bit object should be read. + /// + /// \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); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits