diff --git a/include/llvm/Object/ELF.h b/include/llvm/Object/ELF.h index 46cfee5aec7..f477db64c82 100644 --- a/include/llvm/Object/ELF.h +++ b/include/llvm/Object/ELF.h @@ -105,36 +105,6 @@ private: const Elf_Shdr *SymbolTableSectionHeaderIndex = nullptr; DenseMap ExtendedSymbolTable; - const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version - const Elf_Shdr *dot_gnu_version_r_sec = nullptr; // .gnu.version_r - const Elf_Shdr *dot_gnu_version_d_sec = nullptr; // .gnu.version_d - - // Records for each version index the corresponding Verdef or Vernaux entry. - // This is filled the first time LoadVersionMap() is called. - class VersionMapEntry : public PointerIntPair { - public: - // If the integer is 0, this is an Elf_Verdef*. - // If the integer is 1, this is an Elf_Vernaux*. - VersionMapEntry() : PointerIntPair(nullptr, 0) { } - VersionMapEntry(const Elf_Verdef *verdef) - : PointerIntPair(verdef, 0) { } - VersionMapEntry(const Elf_Vernaux *vernaux) - : PointerIntPair(vernaux, 1) { } - bool isNull() const { return getPointer() == nullptr; } - bool isVerdef() const { return !isNull() && getInt() == 0; } - bool isVernaux() const { return !isNull() && getInt() == 1; } - const Elf_Verdef *getVerdef() const { - return isVerdef() ? (const Elf_Verdef*)getPointer() : nullptr; - } - const Elf_Vernaux *getVernaux() const { - return isVernaux() ? (const Elf_Vernaux*)getPointer() : nullptr; - } - }; - mutable SmallVector VersionMap; - void LoadVersionDefs(const Elf_Shdr *sec) const; - void LoadVersionNeeds(const Elf_Shdr *ec) const; - void LoadVersionMap() const; - public: template const T *getEntry(uint32_t Section, uint32_t Entry) const; @@ -147,8 +117,6 @@ public: ErrorOr getStringTable(const Elf_Shdr *Section) const; ErrorOr getStringTableForSymtab(const Elf_Shdr &Section) const; - ErrorOr getSymbolVersion(StringRef StrTab, const Elf_Sym *Symb, - bool &IsDefault) const; void VerifyStrTab(const Elf_Shdr *sh) const; StringRef getRelocationTypeName(uint32_t Type) const; @@ -282,87 +250,6 @@ typedef ELFFile> ELF64LEFile; typedef ELFFile> ELF32BEFile; typedef ELFFile> ELF64BEFile; -// Iterate through the version definitions, and place each Elf_Verdef -// in the VersionMap according to its index. -template -void ELFFile::LoadVersionDefs(const Elf_Shdr *sec) const { - unsigned vd_size = sec->sh_size; // Size of section in bytes - unsigned vd_count = sec->sh_info; // Number of Verdef entries - const char *sec_start = (const char*)base() + sec->sh_offset; - const char *sec_end = sec_start + vd_size; - // The first Verdef entry is at the start of the section. - const char *p = sec_start; - for (unsigned i = 0; i < vd_count; i++) { - if (p + sizeof(Elf_Verdef) > sec_end) - report_fatal_error("Section ended unexpectedly while scanning " - "version definitions."); - const Elf_Verdef *vd = reinterpret_cast(p); - if (vd->vd_version != ELF::VER_DEF_CURRENT) - report_fatal_error("Unexpected verdef version"); - size_t index = vd->vd_ndx & ELF::VERSYM_VERSION; - if (index >= VersionMap.size()) - VersionMap.resize(index + 1); - VersionMap[index] = VersionMapEntry(vd); - p += vd->vd_next; - } -} - -// Iterate through the versions needed section, and place each Elf_Vernaux -// in the VersionMap according to its index. -template -void ELFFile::LoadVersionNeeds(const Elf_Shdr *sec) const { - unsigned vn_size = sec->sh_size; // Size of section in bytes - unsigned vn_count = sec->sh_info; // Number of Verneed entries - const char *sec_start = (const char *)base() + sec->sh_offset; - const char *sec_end = sec_start + vn_size; - // The first Verneed entry is at the start of the section. - const char *p = sec_start; - for (unsigned i = 0; i < vn_count; i++) { - if (p + sizeof(Elf_Verneed) > sec_end) - report_fatal_error("Section ended unexpectedly while scanning " - "version needed records."); - const Elf_Verneed *vn = reinterpret_cast(p); - if (vn->vn_version != ELF::VER_NEED_CURRENT) - report_fatal_error("Unexpected verneed version"); - // Iterate through the Vernaux entries - const char *paux = p + vn->vn_aux; - for (unsigned j = 0; j < vn->vn_cnt; j++) { - if (paux + sizeof(Elf_Vernaux) > sec_end) - report_fatal_error("Section ended unexpected while scanning auxiliary " - "version needed records."); - const Elf_Vernaux *vna = reinterpret_cast(paux); - size_t index = vna->vna_other & ELF::VERSYM_VERSION; - if (index >= VersionMap.size()) - VersionMap.resize(index + 1); - VersionMap[index] = VersionMapEntry(vna); - paux += vna->vna_next; - } - p += vn->vn_next; - } -} - -template -void ELFFile::LoadVersionMap() const { - // If there is no dynamic symtab or version table, there is nothing to do. - if (!DotDynSymSec || !dot_gnu_version_sec) - return; - - // Has the VersionMap already been loaded? - if (VersionMap.size() > 0) - return; - - // The first two version indexes are reserved. - // Index 0 is LOCAL, index 1 is GLOBAL. - VersionMap.push_back(VersionMapEntry()); - VersionMap.push_back(VersionMapEntry()); - - if (dot_gnu_version_d_sec) - LoadVersionDefs(dot_gnu_version_d_sec); - - if (dot_gnu_version_r_sec) - LoadVersionNeeds(dot_gnu_version_r_sec); -} - template ELF::Elf64_Word ELFFile::getExtendedSymbolTableIndex(const Elf_Sym *symb) const { @@ -530,30 +417,6 @@ ELFFile::ELFFile(StringRef Object, std::error_code &EC) DotDynSymSec = &Sec; break; } - case ELF::SHT_GNU_versym: - if (dot_gnu_version_sec != nullptr) { - // More than one .gnu.version section! - EC = object_error::parse_failed; - return; - } - dot_gnu_version_sec = &Sec; - break; - case ELF::SHT_GNU_verdef: - if (dot_gnu_version_d_sec != nullptr) { - // More than one .gnu.version_d section! - EC = object_error::parse_failed; - return; - } - dot_gnu_version_d_sec = &Sec; - break; - case ELF::SHT_GNU_verneed: - if (dot_gnu_version_r_sec != nullptr) { - // More than one .gnu.version_r section! - EC = object_error::parse_failed; - return; - } - dot_gnu_version_r_sec = &Sec; - break; } } @@ -668,61 +531,6 @@ ELFFile::getSectionName(const Elf_Shdr *Section) const { return StringRef(DotShstrtab.data() + Offset); } -template -ErrorOr ELFFile::getSymbolVersion(StringRef StrTab, - const Elf_Sym *symb, - bool &IsDefault) const { - // This is a dynamic symbol. Look in the GNU symbol version table. - if (!dot_gnu_version_sec) { - // No version table. - IsDefault = false; - return StringRef(""); - } - - // Determine the position in the symbol table of this entry. - size_t entry_index = - (reinterpret_cast(symb) - DotDynSymSec->sh_offset - - reinterpret_cast(base())) / - sizeof(Elf_Sym); - - // Get the corresponding version index entry - const Elf_Versym *vs = getEntry(dot_gnu_version_sec, entry_index); - size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; - - // Special markers for unversioned symbols. - if (version_index == ELF::VER_NDX_LOCAL || - version_index == ELF::VER_NDX_GLOBAL) { - IsDefault = false; - return StringRef(""); - } - - // Lookup this symbol in the version table - LoadVersionMap(); - if (version_index >= VersionMap.size() || VersionMap[version_index].isNull()) - return object_error::parse_failed; - const VersionMapEntry &entry = VersionMap[version_index]; - - // Get the version name string - size_t name_offset; - if (entry.isVerdef()) { - // The first Verdaux entry holds the name. - name_offset = entry.getVerdef()->getAux()->vda_name; - } else { - name_offset = entry.getVernaux()->vna_name; - } - - // Set IsDefault - if (entry.isVerdef()) { - IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); - } else { - IsDefault = false; - } - - if (name_offset >= StrTab.size()) - return object_error::parse_failed; - return StringRef(StrTab.data() + name_offset); -} - /// This function returns the hash value for a symbol in the .dynsym section /// Name of the API remains consistent as specified in the libelf /// REF : http://www.sco.com/developers/gabi/latest/ch5.dynamic.html#hash diff --git a/test/Object/corrupt.test b/test/Object/corrupt.test index 05a8d0fab5c..360e7c8775e 100644 --- a/test/Object/corrupt.test +++ b/test/Object/corrupt.test @@ -23,7 +23,7 @@ SYMNAME: Error reading file: Invalid data was encountered while parsing the file RUN: not llvm-readobj %p/Inputs/corrupt-version.elf-x86_64 -dt \ RUN: 2>&1 | FileCheck --check-prefix=VER %s -VER: Error reading file: Invalid data was encountered while parsing the file. +VER: Error reading file: Invalid version entry. // The file is missing the dynamic string table but has references to it. diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index ddab9fb5b57..efff9682d75 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -78,6 +78,10 @@ private: typedef typename ELFO::Elf_Hash Elf_Hash; typedef typename ELFO::Elf_Ehdr Elf_Ehdr; typedef typename ELFO::uintX_t uintX_t; + typedef typename ELFO::Elf_Versym Elf_Versym; + typedef typename ELFO::Elf_Verneed Elf_Verneed; + typedef typename ELFO::Elf_Vernaux Elf_Vernaux; + typedef typename ELFO::Elf_Verdef Elf_Verdef; /// \brief Represents a region described by entries in the .dynamic table. struct DynRegionInfo { @@ -106,6 +110,12 @@ private: return make_range(dynamic_table_begin(), dynamic_table_end()); } + StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, + bool &IsDefault); + void LoadVersionMap(); + void LoadVersionNeeds(const Elf_Shdr *ec) const; + void LoadVersionDefs(const Elf_Shdr *sec) const; + const ELFO *Obj; DynRegionInfo DynRelaRegion; DynRegionInfo DynamicRegion; @@ -113,6 +123,37 @@ private: const Elf_Sym *DynSymStart = nullptr; StringRef SOName; const Elf_Hash *HashTable = nullptr; + + const Elf_Shdr *dot_gnu_version_sec = nullptr; // .gnu.version + const Elf_Shdr *dot_gnu_version_r_sec = nullptr; // .gnu.version_r + const Elf_Shdr *dot_gnu_version_d_sec = nullptr; // .gnu.version_d + + // Records for each version index the corresponding Verdef or Vernaux entry. + // This is filled the first time LoadVersionMap() is called. + class VersionMapEntry : public PointerIntPair { + public: + // If the integer is 0, this is an Elf_Verdef*. + // If the integer is 1, this is an Elf_Vernaux*. + VersionMapEntry() : PointerIntPair(nullptr, 0) {} + VersionMapEntry(const Elf_Verdef *verdef) + : PointerIntPair(verdef, 0) {} + VersionMapEntry(const Elf_Vernaux *vernaux) + : PointerIntPair(vernaux, 1) {} + bool isNull() const { return getPointer() == nullptr; } + bool isVerdef() const { return !isNull() && getInt() == 0; } + bool isVernaux() const { return !isNull() && getInt() == 1; } + const Elf_Verdef *getVerdef() const { + return isVerdef() ? (const Elf_Verdef *)getPointer() : nullptr; + } + const Elf_Vernaux *getVernaux() const { + return isVernaux() ? (const Elf_Vernaux *)getPointer() : nullptr; + } + }; + mutable SmallVector VersionMap; + +public: + std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable, + bool IsDynamic); }; template T errorOrDefault(ErrorOr Val, T Default = T()) { @@ -159,10 +200,145 @@ std::error_code createELFDumper(const object::ObjectFile *Obj, } // namespace llvm -template -static std::string getFullSymbolName(const ELFO &Obj, - const typename ELFO::Elf_Sym *Symbol, - StringRef StrTable, bool IsDynamic) { +// Iterate through the versions needed section, and place each Elf_Vernaux +// in the VersionMap according to its index. +template +void ELFDumper::LoadVersionNeeds(const Elf_Shdr *sec) const { + unsigned vn_size = sec->sh_size; // Size of section in bytes + unsigned vn_count = sec->sh_info; // Number of Verneed entries + const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_end = sec_start + vn_size; + // The first Verneed entry is at the start of the section. + const char *p = sec_start; + for (unsigned i = 0; i < vn_count; i++) { + if (p + sizeof(Elf_Verneed) > sec_end) + report_fatal_error("Section ended unexpectedly while scanning " + "version needed records."); + const Elf_Verneed *vn = reinterpret_cast(p); + if (vn->vn_version != ELF::VER_NEED_CURRENT) + report_fatal_error("Unexpected verneed version"); + // Iterate through the Vernaux entries + const char *paux = p + vn->vn_aux; + for (unsigned j = 0; j < vn->vn_cnt; j++) { + if (paux + sizeof(Elf_Vernaux) > sec_end) + report_fatal_error("Section ended unexpected while scanning auxiliary " + "version needed records."); + const Elf_Vernaux *vna = reinterpret_cast(paux); + size_t index = vna->vna_other & ELF::VERSYM_VERSION; + if (index >= VersionMap.size()) + VersionMap.resize(index + 1); + VersionMap[index] = VersionMapEntry(vna); + paux += vna->vna_next; + } + p += vn->vn_next; + } +} + +// Iterate through the version definitions, and place each Elf_Verdef +// in the VersionMap according to its index. +template +void ELFDumper::LoadVersionDefs(const Elf_Shdr *sec) const { + unsigned vd_size = sec->sh_size; // Size of section in bytes + unsigned vd_count = sec->sh_info; // Number of Verdef entries + const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_end = sec_start + vd_size; + // The first Verdef entry is at the start of the section. + const char *p = sec_start; + for (unsigned i = 0; i < vd_count; i++) { + if (p + sizeof(Elf_Verdef) > sec_end) + report_fatal_error("Section ended unexpectedly while scanning " + "version definitions."); + const Elf_Verdef *vd = reinterpret_cast(p); + if (vd->vd_version != ELF::VER_DEF_CURRENT) + report_fatal_error("Unexpected verdef version"); + size_t index = vd->vd_ndx & ELF::VERSYM_VERSION; + if (index >= VersionMap.size()) + VersionMap.resize(index + 1); + VersionMap[index] = VersionMapEntry(vd); + p += vd->vd_next; + } +} + +template void ELFDumper::LoadVersionMap() { + // If there is no dynamic symtab or version table, there is nothing to do. + if (!DynSymStart || !dot_gnu_version_sec) + return; + + // Has the VersionMap already been loaded? + if (VersionMap.size() > 0) + return; + + // The first two version indexes are reserved. + // Index 0 is LOCAL, index 1 is GLOBAL. + VersionMap.push_back(VersionMapEntry()); + VersionMap.push_back(VersionMapEntry()); + + if (dot_gnu_version_d_sec) + LoadVersionDefs(dot_gnu_version_d_sec); + + if (dot_gnu_version_r_sec) + LoadVersionNeeds(dot_gnu_version_r_sec); +} + +template +StringRef ELFDumper::getSymbolVersion(StringRef StrTab, + const Elf_Sym *symb, + bool &IsDefault) { + // This is a dynamic symbol. Look in the GNU symbol version table. + if (!dot_gnu_version_sec) { + // No version table. + IsDefault = false; + return StringRef(""); + } + + // Determine the position in the symbol table of this entry. + size_t entry_index = (reinterpret_cast(symb) - + reinterpret_cast(DynSymStart)) / + sizeof(Elf_Sym); + + // Get the corresponding version index entry + const Elf_Versym *vs = + Obj->template getEntry(dot_gnu_version_sec, entry_index); + size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; + + // Special markers for unversioned symbols. + if (version_index == ELF::VER_NDX_LOCAL || + version_index == ELF::VER_NDX_GLOBAL) { + IsDefault = false; + return StringRef(""); + } + + // Lookup this symbol in the version table + LoadVersionMap(); + if (version_index >= VersionMap.size() || VersionMap[version_index].isNull()) + reportError("Invalid version entry"); + const VersionMapEntry &entry = VersionMap[version_index]; + + // Get the version name string + size_t name_offset; + if (entry.isVerdef()) { + // The first Verdaux entry holds the name. + name_offset = entry.getVerdef()->getAux()->vda_name; + } else { + name_offset = entry.getVernaux()->vna_name; + } + + // Set IsDefault + if (entry.isVerdef()) { + IsDefault = !(vs->vs_index & ELF::VERSYM_HIDDEN); + } else { + IsDefault = false; + } + + if (name_offset >= StrTab.size()) + reportError("Invalid string offset"); + return StringRef(StrTab.data() + name_offset); +} + +template +std::string ELFDumper::getFullSymbolName(const Elf_Sym *Symbol, + StringRef StrTable, + bool IsDynamic) { StringRef SymbolName = errorOrDefault(Symbol->getName(StrTable)); if (!IsDynamic) return SymbolName; @@ -170,13 +346,9 @@ static std::string getFullSymbolName(const ELFO &Obj, std::string FullSymbolName(SymbolName); bool IsDefault; - ErrorOr Version = - Obj.getSymbolVersion(StrTable, &*Symbol, IsDefault); - if (Version) { - FullSymbolName += (IsDefault ? "@@" : "@"); - FullSymbolName += *Version; - } else - error(Version.getError()); + StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); + FullSymbolName += (IsDefault ? "@@" : "@"); + FullSymbolName += Version; return FullSymbolName; } @@ -671,6 +843,26 @@ ELFDumper::ELFDumper(const ELFFile *Obj, StreamWriter &Writer) DynamicStringTable = StringRef(StringTableBegin, StringTableSize); if (SONameOffset) SOName = getDynamicString(SONameOffset); + + for (const Elf_Shdr &Sec : Obj->sections()) { + switch (Sec.sh_type) { + case ELF::SHT_GNU_versym: + if (dot_gnu_version_sec != nullptr) + reportError("Multiple SHT_GNU_versym"); + dot_gnu_version_sec = &Sec; + break; + case ELF::SHT_GNU_verdef: + if (dot_gnu_version_d_sec != nullptr) + reportError("Multiple SHT_GNU_verdef"); + dot_gnu_version_d_sec = &Sec; + break; + case ELF::SHT_GNU_verneed: + if (dot_gnu_version_r_sec != nullptr) + reportError("Multilpe SHT_GNU_verneed"); + dot_gnu_version_r_sec = &Sec; + break; + } + } } template @@ -946,8 +1138,7 @@ void ELFDumper::printSymbol(const Elf_Sym *Symbol, StringRef StrTable, unsigned SectionIndex = 0; StringRef SectionName; getSectionNameIndex(*Obj, Symbol, SectionName, SectionIndex); - std::string FullSymbolName = - getFullSymbolName(*Obj, Symbol, StrTable, IsDynamic); + std::string FullSymbolName = getFullSymbolName(Symbol, StrTable, IsDynamic); DictScope D(W, "Symbol"); W.printNumber("Name", FullSymbolName, Symbol->st_name); @@ -1346,12 +1537,14 @@ public: typedef typename ELFO::Elf_Rel Elf_Rel; typedef typename ELFO::Elf_Rela Elf_Rela; - MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, StreamWriter &W); + MipsGOTParser(ELFDumper *Dumper, const ELFO *Obj, + Elf_Dyn_Range DynTable, StreamWriter &W); void parseGOT(); void parsePLT(); private: + ELFDumper *Dumper; const ELFO *Obj; StreamWriter &W; llvm::Optional DtPltGot; @@ -1377,9 +1570,9 @@ private: } template -MipsGOTParser::MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, - StreamWriter &W) - : Obj(Obj), W(W) { +MipsGOTParser::MipsGOTParser(ELFDumper *Dumper, const ELFO *Obj, + Elf_Dyn_Range DynTable, StreamWriter &W) + : Dumper(Dumper), Obj(Obj), W(W) { for (const auto &Entry : DynTable) { switch (Entry.getTag()) { case ELF::DT_PLTGOT: @@ -1606,7 +1799,7 @@ void MipsGOTParser::printGlobalGotEntry( W.printHex("Section", SectionName, SectionIndex); std::string FullSymbolName = - getFullSymbolName(*Obj, Sym, StrTable, IsDynamic); + Dumper->getFullSymbolName(Sym, StrTable, IsDynamic); W.printNumber("Name", FullSymbolName, Sym->st_name); } @@ -1638,7 +1831,7 @@ void MipsGOTParser::printPLTEntry(uint64_t PLTAddr, getSectionNameIndex(*Obj, Sym, SectionName, SectionIndex); W.printHex("Section", SectionName, SectionIndex); - std::string FullSymbolName = getFullSymbolName(*Obj, Sym, StrTable, true); + std::string FullSymbolName = Dumper->getFullSymbolName(Sym, StrTable, true); W.printNumber("Name", FullSymbolName, Sym->st_name); } @@ -1648,7 +1841,7 @@ template void ELFDumper::printMipsPLTGOT() { return; } - MipsGOTParser GOTParser(Obj, dynamic_table(), W); + MipsGOTParser GOTParser(this, Obj, dynamic_table(), W); GOTParser.parseGOT(); GOTParser.parsePLT(); } diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index db0ffd0141a..93fa01c2aa2 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -191,7 +191,7 @@ namespace opts { namespace llvm { void reportError(Twine Msg) { - outs() << Msg << "\n"; + outs() << "\nError reading file: " << Msg << ".\n"; outs().flush(); exit(1); } @@ -200,7 +200,7 @@ void error(std::error_code EC) { if (!EC) return; - reportError(Twine("\nError reading file: ") + EC.message() + "."); + reportError(EC.message()); } bool relocAddressLess(RelocationRef a, RelocationRef b) {