diff options
Diffstat (limited to 'lldb/source/Plugins/ObjectFile/ELF')
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 468 | ||||
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h | 45 |
2 files changed, 463 insertions, 50 deletions
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index 379186a2352..d24ebd44c73 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -23,6 +23,8 @@ #include "lldb/Symbol/SymbolContext.h" #include "lldb/Host/Host.h" +#include "llvm/ADT/PointerUnion.h" + #define CASE_AND_STREAM(s, def, width) \ case def: s->Printf("%-*s", width, #def); break; @@ -31,6 +33,113 @@ using namespace lldb_private; using namespace elf; using namespace llvm::ELF; +namespace { +//===----------------------------------------------------------------------===// +/// @class ELFRelocation +/// @brief Generic wrapper for ELFRel and ELFRela. +/// +/// This helper class allows us to parse both ELFRel and ELFRela relocation +/// entries in a generic manner. +class ELFRelocation +{ +public: + + /// Constructs an ELFRelocation entry with a personality as given by @p + /// type. + /// + /// @param type Either DT_REL or DT_RELA. Any other value is invalid. + ELFRelocation(unsigned type); + + ~ELFRelocation(); + + bool + Parse(const lldb_private::DataExtractor &data, uint32_t *offset); + + static unsigned + RelocType32(const ELFRelocation &rel); + + static unsigned + RelocType64(const ELFRelocation &rel); + + static unsigned + RelocSymbol32(const ELFRelocation &rel); + + static unsigned + RelocSymbol64(const ELFRelocation &rel); + +private: + typedef llvm::PointerUnion<ELFRel*, ELFRela*> RelocUnion; + + RelocUnion reloc; +}; + +ELFRelocation::ELFRelocation(unsigned type) +{ + if (type == DT_REL) + reloc = new ELFRel(); + else if (type == DT_RELA) + reloc = new ELFRela(); + else { + assert(false && "unexpected relocation type"); + reloc = static_cast<ELFRel*>(NULL); + } +} + +ELFRelocation::~ELFRelocation() +{ + if (reloc.is<ELFRel*>()) + delete reloc.get<ELFRel*>(); + else + delete reloc.get<ELFRela*>(); +} + +bool +ELFRelocation::Parse(const lldb_private::DataExtractor &data, uint32_t *offset) +{ + if (reloc.is<ELFRel*>()) + return reloc.get<ELFRel*>()->Parse(data, offset); + else + return reloc.get<ELFRela*>()->Parse(data, offset); +} + +unsigned +ELFRelocation::RelocType32(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocType32(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocType32(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocType64(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocType64(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocType64(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocSymbol32(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocSymbol32(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocSymbol32(*rel.reloc.get<ELFRela*>()); +} + +unsigned +ELFRelocation::RelocSymbol64(const ELFRelocation &rel) +{ + if (rel.reloc.is<ELFRel*>()) + return ELFRel::RelocSymbol64(*rel.reloc.get<ELFRel*>()); + else + return ELFRela::RelocSymbol64(*rel.reloc.get<ELFRela*>()); +} + +} // end anonymous namespace + //------------------------------------------------------------------ // Static methods. //------------------------------------------------------------------ @@ -195,54 +304,54 @@ ObjectFileELF::GetDependentModules(FileSpecList &files) return num_specs; } -Address -ObjectFileELF::GetImageInfoAddress() +user_id_t +ObjectFileELF::GetSectionIndexByType(unsigned type) { if (!ParseSectionHeaders()) - return Address(); + return 0; - user_id_t dynsym_id = 0; for (SectionHeaderCollIter sh_pos = m_section_headers.begin(); sh_pos != m_section_headers.end(); ++sh_pos) { - if (sh_pos->sh_type == SHT_DYNAMIC) - { - dynsym_id = SectionIndex(sh_pos); - break; - } + if (sh_pos->sh_type == type) + return SectionIndex(sh_pos); } - if (!dynsym_id) + return 0; +} + +Address +ObjectFileELF::GetImageInfoAddress() +{ + if (!ParseDynamicSymbols()) return Address(); SectionList *section_list = GetSectionList(); if (!section_list) return Address(); - // Resolve the dynamic table entries. + user_id_t dynsym_id = GetSectionIndexByType(SHT_DYNAMIC); + if (!dynsym_id) + return Address(); + + const ELFSectionHeader *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id); + if (!dynsym_hdr) + return Address(); + Section *dynsym = section_list->FindSectionByID(dynsym_id).get(); if (!dynsym) return Address(); - - DataExtractor dynsym_data; - if (dynsym->ReadSectionDataFromObjectFile(this, dynsym_data)) + + for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) { - ELFDynamic symbol; - const unsigned section_size = dynsym_data.GetByteSize(); - unsigned offset = 0; - unsigned cursor = 0; + ELFDynamic &symbol = m_dynamic_symbols[i]; - // Look for a DT_DEBUG entry. - while (cursor < section_size) + if (symbol.d_tag == DT_DEBUG) { - offset = cursor; - if (!symbol.Parse(dynsym_data, &cursor)) - break; - - if (symbol.d_tag != DT_DEBUG) - continue; - - return Address(dynsym, offset + sizeof(symbol.d_tag)); + // 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, offset); } } @@ -476,6 +585,18 @@ ObjectFileELF::GetSectionIndexByName(const char *name) return 0; } +const elf::ELFSectionHeader * +ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) +{ + if (!ParseSectionHeaders() || !id) + return NULL; + + if (--id < m_section_headers.size()) + return &m_section_headers[id]; + + return NULL; +} + SectionList * ObjectFileELF::GetSectionList() { @@ -548,16 +669,18 @@ ObjectFileELF::GetSectionList() return m_sections_ap.get(); } -static void -ParseSymbols(Symtab *symtab, SectionList *section_list, - const ELFSectionHeader &symtab_shdr, +static unsigned +ParseSymbols(Symtab *symtab, + user_id_t start_id, + SectionList *section_list, + const ELFSectionHeader *symtab_shdr, const DataExtractor &symtab_data, const DataExtractor &strtab_data) { ELFSymbol symbol; uint32_t offset = 0; - const unsigned numSymbols = - symtab_data.GetByteSize() / symtab_shdr.sh_entsize; + const unsigned num_symbols = + symtab_data.GetByteSize() / symtab_shdr->sh_entsize; static ConstString text_section_name(".text"); static ConstString init_section_name(".init"); @@ -571,7 +694,8 @@ ParseSymbols(Symtab *symtab, SectionList *section_list, static ConstString data2_section_name(".data1"); static ConstString bss_section_name(".bss"); - for (unsigned i = 0; i < numSymbols; ++i) + unsigned i; + for (i = 0; i < num_symbols; ++i) { if (symbol.Parse(symtab_data, &offset) == false) break; @@ -658,7 +782,7 @@ ParseSymbols(Symtab *symtab, SectionList *section_list, uint32_t flags = symbol.st_other << 8 | symbol.st_info; Symbol dc_symbol( - i, // ID is the original symbol table index. + i + start_id, // ID is the original symbol table index. symbol_name, // Symbol name. false, // Is the symbol name mangled? symbol_type, // Type of this symbol @@ -672,26 +796,29 @@ ParseSymbols(Symtab *symtab, SectionList *section_list, flags); // Symbol flags. symtab->AddSymbol(dc_symbol); } + + return i; } -void -ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, - const ELFSectionHeader &symtab_hdr, +unsigned +ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, user_id_t start_id, + const ELFSectionHeader *symtab_hdr, user_id_t symtab_id) { - assert(symtab_hdr.sh_type == SHT_SYMTAB || - symtab_hdr.sh_type == SHT_DYNSYM); + assert(symtab_hdr->sh_type == SHT_SYMTAB || + symtab_hdr->sh_type == SHT_DYNSYM); // Parse in the section list if needed. SectionList *section_list = GetSectionList(); if (!section_list) - return; + return 0; // Section ID's are ones based. - user_id_t strtab_id = symtab_hdr.sh_link + 1; + user_id_t strtab_id = symtab_hdr->sh_link + 1; Section *symtab = section_list->FindSectionByID(symtab_id).get(); Section *strtab = section_list->FindSectionByID(strtab_id).get(); + unsigned num_symbols = 0; if (symtab && strtab) { DataExtractor symtab_data; @@ -699,10 +826,244 @@ ObjectFileELF::ParseSymbolTable(Symtab *symbol_table, if (symtab->ReadSectionDataFromObjectFile(this, symtab_data) && strtab->ReadSectionDataFromObjectFile(this, strtab_data)) { - ParseSymbols(symbol_table, section_list, symtab_hdr, - symtab_data, strtab_data); + num_symbols = ParseSymbols(symbol_table, start_id, + section_list, symtab_hdr, + symtab_data, strtab_data); } } + + return num_symbols; +} + +size_t +ObjectFileELF::ParseDynamicSymbols() +{ + if (m_dynamic_symbols.size()) + return m_dynamic_symbols.size(); + + user_id_t dyn_id = GetSectionIndexByType(SHT_DYNAMIC); + if (!dyn_id) + return NULL; + + SectionList *section_list = GetSectionList(); + if (!section_list) + return NULL; + + Section *dynsym = section_list->FindSectionByID(dyn_id).get(); + if (!dynsym) + return NULL; + + ELFDynamic symbol; + DataExtractor dynsym_data; + if (dynsym->ReadSectionDataFromObjectFile(this, dynsym_data)) + { + + const unsigned section_size = dynsym_data.GetByteSize(); + unsigned offset = 0; + unsigned cursor = 0; + + while (cursor < section_size) + { + offset = cursor; + if (!symbol.Parse(dynsym_data, &cursor)) + break; + + m_dynamic_symbols.push_back(symbol); + } + } + + return m_dynamic_symbols.size(); +} + +const ELFDynamic * +ObjectFileELF::FindDynamicSymbol(unsigned tag) +{ + if (!ParseDynamicSymbols()) + return NULL; + + SectionList *section_list = GetSectionList(); + if (!section_list) + return 0; + + 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; + } + + return NULL; +} + +Section * +ObjectFileELF::PLTSection() +{ + const ELFDynamic *symbol = FindDynamicSymbol(DT_JMPREL); + SectionList *section_list = GetSectionList(); + + if (symbol && section_list) + { + addr_t addr = symbol->d_ptr; + return section_list->FindSectionContainingFileAddress(addr).get(); + } + + return NULL; +} + +unsigned +ObjectFileELF::PLTRelocationType() +{ + const ELFDynamic *symbol = FindDynamicSymbol(DT_PLTREL); + + if (symbol) + return symbol->d_val; + + return 0; +} + +static unsigned +ParsePLTRelocations(Symtab *symbol_table, + user_id_t start_id, + unsigned rel_type, + const ELFHeader *hdr, + const ELFSectionHeader *rel_hdr, + const ELFSectionHeader *plt_hdr, + const ELFSectionHeader *sym_hdr, + Section *plt_section, + DataExtractor &rel_data, + DataExtractor &symtab_data, + DataExtractor &strtab_data) +{ + ELFRelocation rel(rel_type); + ELFSymbol symbol; + uint32_t offset = 0; + const unsigned plt_entsize = plt_hdr->sh_entsize; + const unsigned num_relocations = rel_hdr->sh_size / rel_hdr->sh_entsize; + + typedef unsigned (*reloc_info_fn)(const ELFRelocation &rel); + reloc_info_fn reloc_type; + reloc_info_fn reloc_symbol; + + if (hdr->Is32Bit() == 4) + { + reloc_type = ELFRelocation::RelocType32; + reloc_symbol = ELFRelocation::RelocSymbol32; + } + else + { + reloc_type = ELFRelocation::RelocType64; + reloc_symbol = ELFRelocation::RelocSymbol64; + } + + unsigned slot_type = hdr->GetRelocationJumpSlotType(); + unsigned i; + for (i = 0; i < num_relocations; ++i) + { + if (rel.Parse(rel_data, &offset) == false) + break; + + if (reloc_type(rel) != slot_type) + continue; + + unsigned symbol_offset = reloc_symbol(rel) * sym_hdr->sh_entsize; + uint64_t plt_index = (i + 1) * plt_entsize; + + if (!symbol.Parse(symtab_data, &symbol_offset)) + break; + + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + + Symbol jump_symbol( + i + start_id, // Symbol table index + symbol_name, // symbol name. + false, // is the symbol name mangled? + eSymbolTypeTrampoline, // Type of this symbol + false, // Is this globally visible? + false, // Is this symbol debug info? + true, // Is this symbol a trampoline? + true, // Is this symbol artificial? + plt_section, // Section in which this symbol is defined or null. + plt_index, // Offset in section or symbol value. + plt_entsize, // Size in bytes of this symbol. + 0); // Symbol flags. + + symbol_table->AddSymbol(jump_symbol); + } + + return i; +} + +unsigned +ObjectFileELF::ParseTrampolineSymbols(Symtab *symbol_table, + user_id_t start_id, + const ELFSectionHeader *rel_hdr, + user_id_t rel_id) +{ + assert(rel_hdr->sh_type == SHT_RELA || rel_hdr->sh_type == SHT_REL); + + // The link field points to the asscoiated symbol table. The info field + // points to the section holding the plt. + user_id_t symtab_id = rel_hdr->sh_link; + user_id_t plt_id = rel_hdr->sh_info; + + if (!symtab_id || !plt_id) + return 0; + + // Section ID's are ones based; + symtab_id++; + plt_id++; + + const ELFSectionHeader *plt_hdr = GetSectionHeaderByIndex(plt_id); + if (!plt_hdr) + return 0; + + const ELFSectionHeader *sym_hdr = GetSectionHeaderByIndex(symtab_id); + if (!sym_hdr) + return 0; + + SectionList *section_list = GetSectionList(); + if (!section_list) + return 0; + + Section *rel_section = section_list->FindSectionByID(rel_id).get(); + if (!rel_section) + return 0; + + Section *plt_section = section_list->FindSectionByID(plt_id).get(); + if (!plt_section) + return 0; + + Section *symtab = section_list->FindSectionByID(symtab_id).get(); + if (!symtab) + return 0; + + Section *strtab = section_list->FindSectionByID(sym_hdr->sh_link + 1).get(); + if (!strtab) + return 0; + + DataExtractor rel_data; + if (!rel_section->ReadSectionDataFromObjectFile(this, rel_data)) + return 0; + + DataExtractor symtab_data; + if (!symtab->ReadSectionDataFromObjectFile(this, symtab_data)) + return 0; + + DataExtractor strtab_data; + if (!strtab->ReadSectionDataFromObjectFile(this, strtab_data)) + return 0; + + unsigned rel_type = PLTRelocationType(); + if (!rel_type) + return 0; + + return ParsePLTRelocations(symbol_table, start_id, rel_type, + &m_header, rel_hdr, plt_hdr, sym_hdr, + plt_section, + rel_data, symtab_data, strtab_data); } Symtab * @@ -714,22 +1075,35 @@ ObjectFileELF::GetSymtab() Symtab *symbol_table = new Symtab(this); m_symtab_ap.reset(symbol_table); - Mutex::Locker locker (symbol_table->GetMutex ()); + Mutex::Locker locker(symbol_table->GetMutex()); if (!(ParseSectionHeaders() && GetSectionHeaderStringTable())) return symbol_table; // Locate and parse all linker symbol tables. + uint64_t symbol_id = 0; for (SectionHeaderCollIter I = m_section_headers.begin(); I != m_section_headers.end(); ++I) { if (I->sh_type == SHT_SYMTAB) { - const ELFSectionHeader &symtab_section = *I; + const ELFSectionHeader &symtab_header = *I; user_id_t section_id = SectionIndex(I); - ParseSymbolTable (symbol_table, symtab_section, section_id); + symbol_id += ParseSymbolTable(symbol_table, symbol_id, + &symtab_header, section_id); } } + + // Synthesize trampoline symbols to help navigate the PLT. + Section *reloc_section = PLTSection(); + if (reloc_section) + { + user_id_t reloc_id = reloc_section->GetID(); + const ELFSectionHeader *reloc_header = GetSectionHeaderByIndex(reloc_id); + assert(reloc_header); + + ParseTrampolineSymbols(symbol_table, symbol_id, reloc_header, reloc_id); + } return symbol_table; } diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index 2ac4a63cbea..f442ea218ab 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -120,6 +120,10 @@ private: typedef SectionHeaderColl::iterator SectionHeaderCollIter; typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; + typedef std::vector<elf::ELFDynamic> DynamicSymbolColl; + typedef DynamicSymbolColl::iterator DynamicSymbolCollIter; + typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter; + /// Version of this reader common to all plugins based on this class. static const uint32_t m_plugin_version = 1; @@ -132,6 +136,9 @@ private: /// Collection of section headers. SectionHeaderColl m_section_headers; + /// Collection of symbols from the dynamic table. + DynamicSymbolColl m_dynamic_symbols; + /// List of sections present in this ELF object file. mutable std::auto_ptr<lldb_private::SectionList> m_sections_ap; @@ -147,7 +154,7 @@ private: /// Cached value of the entry point for this module. lldb_private::Address m_entry_point_address; - + /// Returns a 1 based index of the given section header. unsigned SectionIndex(const SectionHeaderCollIter &I); @@ -175,13 +182,29 @@ private: size_t ParseDependentModules(); + /// Parses the dynamic symbol table and populates m_dynamic_symbols. The + /// vector retains the order as found in the object file. Returns the + /// number of dynamic symbols parsed. + size_t + ParseDynamicSymbols(); + /// Populates m_symtab_ap will all non-dynamic linker symbols. This method /// will parse the symbols only once. Returns the number of symbols parsed. - void + unsigned ParseSymbolTable(lldb_private::Symtab *symbol_table, - const elf::ELFSectionHeader &symtab_section, + lldb::user_id_t start_id, + const elf::ELFSectionHeader *symtab_section, lldb::user_id_t symtab_id); + /// Scans the relocation entries and adds a set of artificial symbols to the + /// given symbol table for each PLT slot. Returns the number of symbols + /// added. + unsigned + ParseTrampolineSymbols(lldb_private::Symtab *symbol_table, + lldb::user_id_t start_id, + const elf::ELFSectionHeader *rela_hdr, + lldb::user_id_t section_id); + /// Loads the section name string table into m_shstr_data. Returns the /// number of bytes constituting the table. size_t @@ -194,6 +217,14 @@ private: lldb::user_id_t GetSectionIndexByName(const char *name); + // Returns the ID of the first section that has the given type. + lldb::user_id_t + GetSectionIndexByType(unsigned type); + + /// Returns the section header with the given id or NULL. + const elf::ELFSectionHeader * + GetSectionHeaderByIndex(lldb::user_id_t id); + /// @name ELF header dump routines //@{ static void @@ -246,6 +277,14 @@ private: void DumpDependentModules(lldb_private::Stream *s); + const elf::ELFDynamic * + FindDynamicSymbol(unsigned tag); + + lldb_private::Section * + PLTSection(); + + unsigned + PLTRelocationType(); }; #endif // #ifndef liblldb_ObjectFileELF_h_ |