Index: llvm/test/tools/llvm-readobj/elf-verdef-invalid.test =================================================================== --- llvm/test/tools/llvm-readobj/elf-verdef-invalid.test +++ llvm/test/tools/llvm-readobj/elf-verdef-invalid.test @@ -262,3 +262,48 @@ Names: [] DynamicSymbols: - Name: foo + +## Check we error out when trying to print version symbols, but SHT_GNU_verdef is invalid due to any reason. + +# RUN: yaml2obj %s --docnum=10 -o %t10 +# RUN: not llvm-readobj -V %t10 2>&1 | FileCheck %s --check-prefix=INVALID-VERDEF-LLVM -DFILE=%t10 +# RUN: not llvm-readelf -V %t10 2>&1 | FileCheck %s --check-prefix=INVALID-VERDEF-GNU -DFILE=%t10 + +# INVALID-VERDEF-LLVM: VersionSymbols [ +# INVALID-VERDEF-LLVM-NEXT: Symbol { +# INVALID-VERDEF-LLVM-NEXT: Version: 0 +# INVALID-VERDEF-LLVM-NEXT: Name: +# INVALID-VERDEF-LLVM-NEXT: } +# INVALID-VERDEF-LLVM-NEXT: Symbol { +# INVALID-VERDEF-LLVM-EMPTY: +# INVALID-VERDEF-LLVM-NEXT: error: '[[FILE]]': invalid SHT_GNU_verdef section with index 2: version definition 1 goes past the end of the section + +# INVALID-VERDEF-GNU: Version symbols section '.gnu.version' contains 2 entries: +# INVALID-VERDEF-GNU-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 5 (.dynsym) +# INVALID-VERDEF-GNU-NEXT: 000: 0 (*local*) +# INVALID-VERDEF-GNU-NEXT: error: '[[FILE]]': invalid SHT_GNU_verdef section with index 2: version definition 1 goes past the end of the section + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Link: .dynsym + AddressAlign: 0x0000000000000002 + EntSize: 0x0000000000000002 + Entries: [ 0, 2 ] + - Name: .gnu.version_d + Type: SHT_GNU_verdef + Flags: [ SHF_ALLOC ] + Link: .dynstr + AddressAlign: 0x4 + Info: 0x1 + Entries: [] +DynamicSymbols: + - Name: foo + Binding: STB_GLOBAL Index: llvm/test/tools/llvm-readobj/elf-verneed-invalid.test =================================================================== --- llvm/test/tools/llvm-readobj/elf-verneed-invalid.test +++ llvm/test/tools/llvm-readobj/elf-verneed-invalid.test @@ -86,11 +86,12 @@ # GNU-NOLINK: Version symbols section '.gnu.version' contains 2 entries: # GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 5 (.dynsym) -# GNU-NOLINK-NEXT: 000: 0 (*local*) 2 (bar) +# GNU-NOLINK-NEXT: 000: 0 (*local*) +# GNU-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL +# GNU-NOLINK-NEXT: 2 () +# GNU-NOLINK-EMPTY: # GNU-NOLINK: Version needs section '.gnu.version_r' contains 1 entries: # GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 0 () -# GNU-NOLINK-EMPTY: -# GNU-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL # GNU-NOLINK-NEXT: 0x0000: Version: 1 File: Cnt: 1 # GNU-NOLINK-NEXT: 0x0010: Name: Flags: none Version: 2 @@ -100,14 +101,14 @@ # LLVM-NOLINK-NEXT: Name: # LLVM-NOLINK-NEXT: } # LLVM-NOLINK-NEXT: Symbol { +# LLVM-NOLINK-EMPTY: +# LLVM-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL # LLVM-NOLINK-NEXT: Version: 2 -# LLVM-NOLINK-NEXT: Name: foo@bar +# LLVM-NOLINK-NEXT: Name: foo@ # LLVM-NOLINK-NEXT: } # LLVM-NOLINK-NEXT: ] # LLVM-NOLINK: VersionRequirements [ -# LLVM-NOLINK-EMPTY: -# LLVM-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL # LLVM-NOLINK-NEXT: Dependency { # LLVM-NOLINK-NEXT: Version: 1 # LLVM-NOLINK-NEXT: Count: 1 @@ -155,14 +156,12 @@ Binding: STB_GLOBAL ## We can't parse misaligned auxiliary version records. -## Here we have a SHT_GNU_verneed section aligned by 1 byte. -## This makes the first auxiliary record offset % 4 be non-zero. # RUN: yaml2obj --docnum=3 %s -o %t3 # RUN: not llvm-readelf -V %t3 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=BROKEN-AUX # RUN: not llvm-readobj -V %t3 2>&1 | FileCheck %s -DFILE=%t3 --check-prefix=BROKEN-AUX -# BROKEN-AUX: error: '[[FILE]]': SHT_GNU_verneed: the vn_aux field of the entry with index 0 references a misaligned auxiliary record +# BROKEN-AUX: error: '[[FILE]]': invalid SHT_GNU_verneed section with index 2: found a misaligned auxiliary entry at offset 0x11 --- !ELF FileHeader: @@ -176,19 +175,14 @@ Flags: [ SHF_ALLOC ] Link: .dynsym Entries: [ 2 ] - - Name: .gnu.version_r - Type: SHT_GNU_verneed - Flags: [ SHF_ALLOC ] - Info: 1 - AddressAlign: 1 - Dependencies: - - Version: 1 - File: somefile - Entries: - - Name: 'bar' - Hash: 0 - Flags: 0 - Other: 2 + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Info: 1 + Link: .dynstr + AddressAlign: 4 +## The byte offset to the auxiliary entry is 0x11, i.e. it is not correctly aligned in memory. + Content: "0100010001000000110000000000000000000000" DynamicSymbols: - Name: foo @@ -551,3 +545,63 @@ Other: 0 DynamicSymbols: - Name: foo + +## In this case SHT_GNU_verneed is linked to the custom dynamic string table, which name +## is not ".dynstr". Check we handle this case properly. + +# RUN: yaml2obj --docnum=13 %s -o %t13 +# RUN: llvm-readelf -V %t13 2>&1 | FileCheck %s -DFILE=%t13 --check-prefix=GNU-CUSTOM-DYNSTR +# RUN: llvm-readobj -V %t13 2>&1 | FileCheck %s -DFILE=%t13 --check-prefix=LLVM-CUSTOM-DYNSTR + +# GNU-CUSTOM-DYNSTR: Version symbols section '.gnu.version' contains 2 entries: +# GNU-CUSTOM-DYNSTR-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 6 (.dynsym) +# GNU-CUSTOM-DYNSTR-NEXT: 000: 0 (*local*) 2 (bcdefghij) +# GNU-CUSTOM-DYNSTR: Version needs section '.gnu.version_r' contains 1 entries: +# GNU-CUSTOM-DYNSTR-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 3 (.custom.dynstr) +# GNU-CUSTOM-DYNSTR-NEXT: 0x0000: Version: 1 File: j Cnt: 1 +# GNU-CUSTOM-DYNSTR-NEXT: 0x0010: Name: bcdefghij Flags: none Version: 2 + +# LLVM-CUSTOM-DYNSTR: VersionSymbols [ +# LLVM-CUSTOM-DYNSTR: Symbol { +# LLVM-CUSTOM-DYNSTR: Version: 2 +# LLVM-CUSTOM-DYNSTR-NEXT: Name: foo@bcdefghij + +# LLVM-CUSTOM-DYNSTR: VersionRequirements [ +# LLVM-CUSTOM-DYNSTR: Dependency { +# LLVM-CUSTOM-DYNSTR: Entries [ +# LLVM-CUSTOM-DYNSTR: Entry { +# LLVM-CUSTOM-DYNSTR: Index: 2 +# LLVM-CUSTOM-DYNSTR-NEXT: Name: bcdefghij + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Link: .dynsym + Entries: [ 0, 2 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Link: .custom.dynstr + Info: 1 + AddressAlign: 4 + Dependencies: + - Version: 1 + File: zed + Entries: + - Name: 'bar' + Hash: 0 + Flags: 0 + Other: 2 + - Name: .custom.dynstr + Type: SHT_STRTAB + Content: "6162636465666768696a00" ## 'a','b','c','d','e','f','g','h','i','j',NIL +DynamicSymbols: + - Name: foo + Binding: STB_GLOBAL Index: llvm/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/ELFDumper.cpp +++ llvm/tools/llvm-readobj/ELFDumper.cpp @@ -258,11 +258,8 @@ void loadDynamicTable(const ELFFile *Obj); void parseDynamicTable(); - StringRef getSymbolVersion(StringRef StrTab, const Elf_Sym *symb, - bool &IsDefault) const; + StringRef getSymbolVersion(const Elf_Sym *symb, bool &IsDefault) const; void LoadVersionMap() const; - void LoadVersionNeeds(const Elf_Shdr *ec) const; - void LoadVersionDefs(const Elf_Shdr *sec) const; const object::ELFObjectFile *ObjF; DynRegionInfo DynRelRegion; @@ -285,29 +282,11 @@ const Elf_Shdr *SymbolVersionNeedSection = nullptr; // .gnu.version_r const Elf_Shdr *SymbolVersionDefSection = 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; - } + struct VersionEntry { + std::string Name; + bool IsVerDef; }; - mutable SmallVector VersionMap; + mutable SmallVector, 16> VersionMap; public: Elf_Dyn_Range dynamic_table() const { @@ -340,8 +319,7 @@ unsigned SectionIndex) const; Expected getStaticSymbolName(uint32_t Index) const; std::string getDynamicString(uint64_t Value) const; - StringRef getSymbolVersionByIndex(StringRef StrTab, - uint32_t VersionSymbolIndex, + StringRef getSymbolVersionByIndex(uint32_t VersionSymbolIndex, bool &IsDefault) const; void printSymbolsHelper(bool IsDynamic) const; @@ -909,78 +887,6 @@ } // end namespace llvm -// 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 VerneedSize = Sec->sh_size; // Size of section in bytes - unsigned VerneedEntries = Sec->sh_info; // Number of Verneed entries - const uint8_t *VerneedStart = reinterpret_cast( - ObjF->getELFFile()->base() + Sec->sh_offset); - const uint8_t *VerneedEnd = VerneedStart + VerneedSize; - // The first Verneed entry is at the start of the section. - const uint8_t *VerneedBuf = VerneedStart; - for (unsigned VerneedIndex = 0; VerneedIndex < VerneedEntries; - ++VerneedIndex) { - if (VerneedBuf + sizeof(Elf_Verneed) > VerneedEnd) - report_fatal_error("Section ended unexpectedly while scanning " - "version needed records."); - const Elf_Verneed *Verneed = - reinterpret_cast(VerneedBuf); - if (Verneed->vn_version != ELF::VER_NEED_CURRENT) - report_fatal_error("Unexpected verneed version"); - // Iterate through the Vernaux entries - const uint8_t *VernauxBuf = VerneedBuf + Verneed->vn_aux; - for (unsigned VernauxIndex = 0; VernauxIndex < Verneed->vn_cnt; - ++VernauxIndex) { - if (VernauxBuf + sizeof(Elf_Vernaux) > VerneedEnd) - report_fatal_error("Section ended unexpected while scanning auxiliary " - "version needed records."); - if ((ptrdiff_t)VernauxBuf % sizeof(uint32_t) != 0) - reportError(createError("SHT_GNU_verneed: the vn_aux field of the " - "entry with index " + - Twine(VerneedIndex) + - " references a misaligned auxiliary record"), - ObjF->getFileName()); - - const Elf_Vernaux *Vernaux = - reinterpret_cast(VernauxBuf); - size_t Index = Vernaux->vna_other & ELF::VERSYM_VERSION; - if (Index >= VersionMap.size()) - VersionMap.resize(Index + 1); - VersionMap[Index] = VersionMapEntry(Vernaux); - VernauxBuf += Vernaux->vna_next; - } - VerneedBuf += Verneed->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 VerdefSize = Sec->sh_size; // Size of section in bytes - unsigned VerdefEntries = Sec->sh_info; // Number of Verdef entries - const uint8_t *VerdefStart = reinterpret_cast( - ObjF->getELFFile()->base() + Sec->sh_offset); - const uint8_t *VerdefEnd = VerdefStart + VerdefSize; - // The first Verdef entry is at the start of the section. - const uint8_t *VerdefBuf = VerdefStart; - for (unsigned VerdefIndex = 0; VerdefIndex < VerdefEntries; ++VerdefIndex) { - if (VerdefBuf + sizeof(Elf_Verdef) > VerdefEnd) - report_fatal_error("Section ended unexpectedly while scanning " - "version definitions."); - const Elf_Verdef *Verdef = reinterpret_cast(VerdefBuf); - if (Verdef->vd_version != ELF::VER_DEF_CURRENT) - report_fatal_error("Unexpected verdef version"); - size_t Index = Verdef->vd_ndx & ELF::VERSYM_VERSION; - if (Index >= VersionMap.size()) - VersionMap.resize(Index + 1); - VersionMap[Index] = VersionMapEntry(Verdef); - VerdefBuf += Verdef->vd_next; - } -} - template void ELFDumper::LoadVersionMap() const { // If there is no dynamic symtab or version table, there is nothing to do. if (!DynSymRegion.Addr || !SymbolVersionSection) @@ -992,19 +898,37 @@ // The first two version indexes are reserved. // Index 0 is LOCAL, index 1 is GLOBAL. - VersionMap.push_back(VersionMapEntry()); - VersionMap.push_back(VersionMapEntry()); + VersionMap.push_back(VersionEntry()); + VersionMap.push_back(VersionEntry()); - if (SymbolVersionDefSection) - LoadVersionDefs(SymbolVersionDefSection); + auto InsertEntry = [this](unsigned N, StringRef Version, bool IsVerdef) { + if (N >= VersionMap.size()) + VersionMap.resize(N + 1); + VersionMap[N] = {Version, IsVerdef}; + }; - if (SymbolVersionNeedSection) - LoadVersionNeeds(SymbolVersionNeedSection); + if (SymbolVersionDefSection) { + Expected> Defs = + this->getVersionDefinitions(SymbolVersionDefSection); + if (!Defs) + reportError(Defs.takeError(), ObjF->getFileName()); + for (const VerDef &Def : *Defs) + InsertEntry(Def.Ndx & ELF::VERSYM_VERSION, Def.Name, true); + } + + if (SymbolVersionNeedSection) { + Expected> Deps = + this->getVersionDependencies(SymbolVersionNeedSection); + if (!Deps) + reportError(Deps.takeError(), ObjF->getFileName()); + for (const VerNeed &Dep : *Deps) + for (const VernAux &Aux : Dep.AuxV) + InsertEntry(Aux.Other & ELF::VERSYM_VERSION, Aux.Name, false); + } } template -StringRef ELFDumper::getSymbolVersion(StringRef StrTab, - const Elf_Sym *Sym, +StringRef ELFDumper::getSymbolVersion(const Elf_Sym *Sym, bool &IsDefault) const { // This is a dynamic symbol. Look in the GNU symbol version table. if (!SymbolVersionSection) { @@ -1022,7 +946,7 @@ const Elf_Versym *Versym = unwrapOrError( ObjF->getFileName(), ObjF->getELFFile()->template getEntry( SymbolVersionSection, EntryIndex)); - return this->getSymbolVersionByIndex(StrTab, Versym->vs_index, IsDefault); + return this->getSymbolVersionByIndex(Versym->vs_index, IsDefault); } static std::string maybeDemangle(StringRef Name) { @@ -1049,8 +973,7 @@ } template -StringRef ELFDumper::getSymbolVersionByIndex(StringRef StrTab, - uint32_t SymbolVersionIndex, +StringRef ELFDumper::getSymbolVersionByIndex(uint32_t SymbolVersionIndex, bool &IsDefault) const { size_t VersionIndex = SymbolVersionIndex & VERSYM_VERSION; @@ -1062,23 +985,15 @@ // Lookup this symbol in the version table. LoadVersionMap(); - if (VersionIndex >= VersionMap.size() || VersionMap[VersionIndex].isNull()) + if (VersionIndex >= VersionMap.size() || !VersionMap[VersionIndex]) reportError(createError("Invalid version entry"), ObjF->getFileName()); - const VersionMapEntry &Entry = VersionMap[VersionIndex]; - // Get the version name string. - size_t NameOffset; - if (Entry.isVerdef()) { - // The first Verdaux entry holds the name. - NameOffset = Entry.getVerdef()->getAux()->vda_name; + const VersionEntry &Entry = *VersionMap[VersionIndex]; + if (Entry.IsVerDef) IsDefault = !(SymbolVersionIndex & VERSYM_HIDDEN); - } else { - NameOffset = Entry.getVernaux()->vna_name; + else IsDefault = false; - } - if (NameOffset >= StrTab.size()) - reportError(createError("Invalid string offset"), ObjF->getFileName()); - return StrTab.data() + NameOffset; + return Entry.Name.c_str(); } template @@ -1109,7 +1024,7 @@ return SymbolName; bool IsDefault; - StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); + StringRef Version = getSymbolVersion(&*Symbol, IsDefault); if (!Version.empty()) { SymbolName += (IsDefault ? "@@" : "@"); SymbolName += Version; @@ -4100,7 +4015,6 @@ const uint8_t *VersymBuf = reinterpret_cast(Obj->base() + Sec->sh_offset); const ELFDumper *Dumper = this->dumper(); - StringRef StrTable = Dumper->getDynamicStringTable(); // readelf prints 4 entries per line. for (uint64_t VersymRow = 0; VersymRow < Entries; VersymRow += 4) { @@ -4119,17 +4033,17 @@ OS << " 1 (*global*) "; break; default: - OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION, - Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' '); - bool IsDefault = true; - std::string VersionName = Dumper->getSymbolVersionByIndex( - StrTable, Versym->vs_index, IsDefault); + std::string VersionName = + Dumper->getSymbolVersionByIndex(Versym->vs_index, IsDefault); if (!VersionName.empty()) VersionName = "(" + VersionName + ")"; else VersionName = "(*invalid*)"; + + OS << format("%4x%c", Versym->vs_index & VERSYM_VERSION, + Versym->vs_index & VERSYM_HIDDEN ? 'h' : ' '); OS << left_justify(VersionName, 13); } VersymBuf += sizeof(Elf_Versym);