diff options
Diffstat (limited to 'lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp')
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 182 |
1 files changed, 161 insertions, 21 deletions
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 9c7dff8..8df2268 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -44,6 +44,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MipsABIFlags.h" +#include "lldb/Target/Process.h" #define CASE_AND_STREAM(s, def, width) \ case def: \ @@ -3007,9 +3008,10 @@ void ObjectFileELF::ParseSymtab(Symtab &lldb_symtab) { // section, nomatter if .symtab was already parsed or not. This is because // minidebuginfo normally removes the .symtab symbols which have their // matching .dynsym counterparts. + Section *dynsym = nullptr; if (!symtab || GetSectionList()->FindSectionByName(ConstString(".gnu_debugdata"))) { - Section *dynsym = + dynsym = section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true) .get(); if (dynsym) { @@ -3019,6 +3021,20 @@ void ObjectFileELF::ParseSymtab(Symtab &lldb_symtab) { m_address_class_map.merge(address_class_map); } } + if (!dynsym) { + // Try and read the dynamic symbol table from the .dynamic section. + uint32_t num_symbols = 0; + std::optional<DataExtractor> symtab_data = + GetDynsymDataFromDynamic(num_symbols); + std::optional<DataExtractor> strtab_data = GetDynstrData(); + if (symtab_data && strtab_data) { + auto [num_symbols_parsed, address_class_map] = + ParseSymbols(&lldb_symtab, symbol_id, section_list, num_symbols, + symtab_data.value(), strtab_data.value()); + symbol_id += num_symbols_parsed; + m_address_class_map.merge(address_class_map); + } + } // DT_JMPREL // If present, this entry's d_ptr member holds the address of @@ -3828,6 +3844,33 @@ ObjectFileELF::MapFileDataWritable(const FileSpec &file, uint64_t Size, Offset); } +std::optional<DataExtractor> +ObjectFileELF::ReadDataFromDynamic(const ELFDynamic *dyn, uint64_t length, + uint64_t offset) { + // ELFDynamic values contain a "d_ptr" member that will be a load address if + // we have an ELF file read from memory, or it will be a file address if it + // was read from a ELF file. This function will correctly fetch data pointed + // to by the ELFDynamic::d_ptr, or return std::nullopt if the data isn't + // available. + const lldb::addr_t d_ptr_addr = dyn->d_ptr + offset; + if (ProcessSP process_sp = m_process_wp.lock()) { + if (DataBufferSP data_sp = ReadMemory(process_sp, d_ptr_addr, length)) + 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(d_ptr_addr, GetSectionList())) + return std::nullopt; + DataExtractor data; + addr.GetSection()->GetSectionData(data); + return DataExtractor(data, + d_ptr_addr - addr.GetSection()->GetFileAddress(), + length); + } + return std::nullopt; +} + std::optional<DataExtractor> ObjectFileELF::GetDynstrData() { if (SectionList *section_list = GetSectionList()) { // Find the SHT_DYNAMIC section. @@ -3855,31 +3898,15 @@ std::optional<DataExtractor> ObjectFileELF::GetDynstrData() { // 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. + // When loading and ELF file from memory, only the program headers are + // guaranteed 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; + return ReadDataFromDynamic(strtab, strsz->d_val, /*offset=*/0); } std::optional<lldb_private::DataExtractor> ObjectFileELF::GetDynamicData() { @@ -3912,3 +3939,116 @@ std::optional<lldb_private::DataExtractor> ObjectFileELF::GetDynamicData() { } return std::nullopt; } + +std::optional<uint32_t> ObjectFileELF::GetNumSymbolsFromDynamicHash() { + const ELFDynamic *hash = FindDynamicSymbol(DT_HASH); + if (hash == nullptr) + return std::nullopt; + + // The DT_HASH header looks like this: + struct DtHashHeader { + uint32_t nbucket; + uint32_t nchain; + }; + if (auto data = ReadDataFromDynamic(hash, 8)) { + // We don't need the number of buckets value "nbucket", we just need the + // "nchain" value which contains the number of symbols. + offset_t offset = offsetof(DtHashHeader, nchain); + return data->GetU32(&offset); + } + + return std::nullopt; +} + +std::optional<uint32_t> ObjectFileELF::GetNumSymbolsFromDynamicGnuHash() { + const ELFDynamic *gnu_hash = FindDynamicSymbol(DT_GNU_HASH); + if (gnu_hash == nullptr) + return std::nullopt; + + // Create a DT_GNU_HASH header + // https://flapenguin.me/elf-dt-gnu-hash + struct DtGnuHashHeader { + uint32_t nbuckets = 0; + uint32_t symoffset = 0; + uint32_t bloom_size = 0; + uint32_t bloom_shift = 0; + }; + uint32_t num_symbols = 0; + // Read enogh data for the DT_GNU_HASH header so we can extract the values. + if (auto data = ReadDataFromDynamic(gnu_hash, sizeof(DtGnuHashHeader))) { + offset_t offset = 0; + DtGnuHashHeader header; + header.nbuckets = data->GetU32(&offset); + header.symoffset = data->GetU32(&offset); + header.bloom_size = data->GetU32(&offset); + header.bloom_shift = data->GetU32(&offset); + const size_t addr_size = GetAddressByteSize(); + const addr_t buckets_offset = + sizeof(DtGnuHashHeader) + addr_size * header.bloom_size; + std::vector<uint32_t> buckets; + if (auto bucket_data = ReadDataFromDynamic(gnu_hash, header.nbuckets * 4, buckets_offset)) { + offset = 0; + for (uint32_t i = 0; i < header.nbuckets; ++i) + buckets.push_back(bucket_data->GetU32(&offset)); + // Locate the chain that handles the largest index bucket. + uint32_t last_symbol = 0; + for (uint32_t bucket_value : buckets) + last_symbol = std::max(bucket_value, last_symbol); + if (last_symbol < header.symoffset) { + num_symbols = header.symoffset; + } else { + // Walk the bucket's chain to add the chain length to the total. + const addr_t chains_base_offset = buckets_offset + header.nbuckets * 4; + for (;;) { + if (auto chain_entry_data = ReadDataFromDynamic(gnu_hash, 4, chains_base_offset + (last_symbol - header.symoffset) * 4)) { + offset = 0; + uint32_t chain_entry = chain_entry_data->GetU32(&offset); + ++last_symbol; + // If the low bit is set, this entry is the end of the chain. + if (chain_entry & 1) + break; + } else { + break; + } + } + num_symbols = last_symbol; + } + } + } + if (num_symbols > 0) + return num_symbols; + + return std::nullopt; +} + +std::optional<DataExtractor> +ObjectFileELF::GetDynsymDataFromDynamic(uint32_t &num_symbols) { + // Every ELF file which represents an executable or shared library has + // mandatory .dynamic entries. The DT_SYMTAB value contains a pointer to the + // symbol table, and DT_SYMENT contains the size of a symbol table entry. + // We then can use either the DT_HASH or DT_GNU_HASH to find the number of + // symbols in the symbol table as the symbol count is not stored in the + // .dynamic section as a key/value pair. + // + // 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. + num_symbols = 0; + // Get the process in case this is an in memory ELF file. + ProcessSP process_sp(m_process_wp.lock()); + const ELFDynamic *symtab = FindDynamicSymbol(DT_SYMTAB); + const ELFDynamic *syment = FindDynamicSymbol(DT_SYMENT); + // DT_SYMTAB and DT_SYMENT are mandatory. + if (symtab == nullptr || syment == nullptr) + return std::nullopt; + + if (std::optional<uint32_t> syms = GetNumSymbolsFromDynamicHash()) + num_symbols = *syms; + else if (std::optional<uint32_t> syms = GetNumSymbolsFromDynamicGnuHash()) + num_symbols = *syms; + else + return std::nullopt; + if (num_symbols == 0) + return std::nullopt; + return ReadDataFromDynamic(symtab, syment->d_val * num_symbols); +} |