diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-table.test b/llvm/test/tools/llvm-readobj/ELF/hash-table.test --- a/llvm/test/tools/llvm-readobj/ELF/hash-table.test +++ b/llvm/test/tools/llvm-readobj/ELF/hash-table.test @@ -18,16 +18,17 @@ --- !ELF FileHeader: - Class: ELFCLASS[[BITS]] + Class: ELFCLASS[[BITS=64]] Data: ELFDATA2LSB Type: ET_DYN Machine: [[MACHINE]] Sections: - - Name: .hash - Type: SHT_HASH - Flags: [ SHF_ALLOC ] - Bucket: [ 1, 2 ] - Chain: [ 3, 4, 5 ] + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Bucket: [ 1, 2 ] + Chain: [ 3, 4, 5 ] + EntSize: [[ENTSIZE=4]] - Name: .dynamic Type: SHT_DYNAMIC Flags: [ SHF_ALLOC ] @@ -42,6 +43,43 @@ - Section: .hash - Section: .dynamic +## Document that we ignore the sh_entsize value when dumping the hash section. +## Implementation assumes that the size of entries is 4, matching the ELF specification. + +# RUN: yaml2obj --docnum=1 -DENTSIZE=8 -DBITS=64 -DMACHINE=EM_X86_64 %s -o %t.x64.ent8 +# RUN: yaml2obj --docnum=1 -DENTSIZE=8 -DBITS=32 -DMACHINE=EM_386 %s -o %t.x32.ent8 + +# RUN: llvm-readobj --hash-table %t.x64.ent8 | FileCheck %s --check-prefix=HASH +# RUN: llvm-readelf --hash-table %t.x64.ent8 | FileCheck %s --check-prefix=HASH +# RUN: llvm-readobj --hash-table %t.x32.ent8 | FileCheck %s --check-prefix=HASH +# RUN: llvm-readelf --hash-table %t.x32.ent8 | FileCheck %s --check-prefix=HASH + +## We don't support dumping hash tables on EM_S390 and EM_ALPHA platforms and report a warning. +## On these platforms the size of entries is 8, which violates the ELF specification, which says that the size +## of hash entries in the hash table must be 4. + +# RUN: yaml2obj --docnum=1 -DMACHINE=EM_S390 %s -o %t.s390 +# RUN: llvm-readobj --hash-table %t.s390 2>&1 | FileCheck %s -DFILE=%t.s390 --check-prefixes=WARN-HASH -DNAME="IBM S/390" +# RUN: llvm-readelf --hash-table %t.s390 2>&1 | FileCheck %s -DFILE=%t.s390 --check-prefixes=WARN-HASH -DNAME="IBM S/390" + +# WARN-HASH: HashTable { +# WARN-HASH-NEXT: warning: '[[FILE]]': the hash table at 0x78 is not supported: it contains non-standard 8 byte entries on [[NAME]] platform +# WARN-HASH-NEXT: } + +# RUN: yaml2obj --docnum=1 -DMACHINE=EM_ALPHA %s -o %t.alpha +# RUN: llvm-readobj --hash-table %t.alpha 2>&1 | FileCheck %s -DFILE=%t.alpha --check-prefixes=WARN-HASH -DNAME="EM_ALPHA" +# RUN: llvm-readelf --hash-table %t.alpha 2>&1 | FileCheck %s -DFILE=%t.alpha --check-prefixes=WARN-HASH -DNAME="EM_ALPHA" + +## We don't report warnings about the unsupported hash table on EM_S390 and EM_ALPHA platforms +## when --hash-table is not requested. + +# RUN: llvm-readobj %t.s390 2>&1 | FileCheck %s -DFILE=%t.s390 --implicit-check-not="warning:" --check-prefix=NOWARN +# RUN: llvm-readelf %t.s390 2>&1 | FileCheck %s -DFILE=%t.s390 --implicit-check-not="warning:" --check-prefix=NOWARN +# RUN: llvm-readobj %t.alpha 2>&1 | FileCheck %s -DFILE=%t.alpha --implicit-check-not="warning:" --check-prefix=NOWARN +# RUN: llvm-readelf %t.alpha 2>&1 | FileCheck %s -DFILE=%t.alpha --implicit-check-not="warning:" --check-prefix=NOWARN + +# NOWARN: warning: '[[FILE]]': string table was not found + ## Check we can dump the SHT_HASH section even when an object ## does not have the section header table. diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -328,6 +328,15 @@ std::string describe(const Elf_Shdr &Sec) const; public: + unsigned getHashTableEntSize() const { + // EM_S390 and ELF::EM_ALPHA platforms use 8-bytes entries in SHT_HASH + // sections. This violates the ELF specification. + if (Obj.getHeader().e_machine == ELF::EM_S390 || + Obj.getHeader().e_machine == ELF::EM_ALPHA) + return 8; + return 4; + } + Elf_Dyn_Range dynamic_table() const { // A valid .dynamic section contains an array of entries terminated // with a DT_NULL entry. However, sometimes the section content may @@ -2176,6 +2185,7 @@ SOName = getDynamicString(SONameOffset); + const bool IsHashTableSupported = getHashTableEntSize() == 4; if (DynSymRegion) { // Often we find the information about the dynamic symbol table // location in the SHT_DYNSYM section header. However, the value in @@ -2191,7 +2201,7 @@ // equal nchain". Check to see if the DT_HASH hash table nchain value // conflicts with the number of symbols in the dynamic symbol table // according to the section header. - if (HashTable) { + if (HashTable && IsHashTableSupported) { if (DynSymRegion->EntSize == 0) reportUniqueWarning( createError("SHT_DYNSYM section has sh_entsize == 0")); @@ -2219,7 +2229,7 @@ // Derive the dynamic symbol table size from the DT_HASH hash table, if // present. - if (HashTable && DynSymRegion) { + if (HashTable && IsHashTableSupported && DynSymRegion) { const uint64_t FileSize = Obj.getBufSize(); const uint64_t DerivedSize = (uint64_t)HashTable->nchain * DynSymRegion->EntSize; @@ -2653,29 +2663,43 @@ } template -static Error checkHashTable(const ELFFile &Obj, +static Error checkHashTable(const ELFDumper &Dumper, const typename ELFT::Hash *H, bool *IsHeaderValid = nullptr) { - auto MakeError = [&](uint64_t Off, const Twine &Msg = "") { - return createError("the hash table at offset 0x" + Twine::utohexstr(Off) + + const ELFFile &Obj = *Dumper.getElfObject().getELFFile(); + const uint64_t SecOffset = (const uint8_t *)H - Obj.base(); + if (Dumper.getHashTableEntSize() == 8) { + auto It = llvm::find_if(ElfMachineType, [&](const EnumEntry &E) { + return E.Value == Obj.getHeader().e_machine; + }); + if (IsHeaderValid) + *IsHeaderValid = false; + return createError("the hash table at 0x" + Twine::utohexstr(SecOffset) + + " is not supported: it contains non-standard 8 " + "byte entries on " + + It->AltName + " platform"); + } + + auto MakeError = [&](const Twine &Msg = "") { + return createError("the hash table at offset 0x" + + Twine::utohexstr(SecOffset) + " goes past the end of the file (0x" + Twine::utohexstr(Obj.getBufSize()) + ")" + Msg); }; // Each SHT_HASH section starts from two 32-bit fields: nbucket and nchain. const unsigned HeaderSize = 2 * sizeof(typename ELFT::Word); - const uint64_t SecOffset = (const uint8_t *)H - Obj.base(); if (IsHeaderValid) *IsHeaderValid = Obj.getBufSize() - SecOffset >= HeaderSize; if (Obj.getBufSize() - SecOffset < HeaderSize) - return MakeError(SecOffset); + return MakeError(); if (Obj.getBufSize() - SecOffset - HeaderSize < ((uint64_t)H->nbucket + H->nchain) * sizeof(typename ELFT::Word)) - return MakeError(SecOffset, ", nbucket = " + Twine(H->nbucket) + - ", nchain = " + Twine(H->nchain)); + return MakeError(", nbucket = " + Twine(H->nbucket) + + ", nchain = " + Twine(H->nchain)); return Error::success(); } @@ -2706,7 +2730,7 @@ return; bool IsHeaderValid; - Error Err = checkHashTable(Obj, HashTable, &IsHeaderValid); + Error Err = checkHashTable(*this, HashTable, &IsHeaderValid); if (IsHeaderValid) { W.printNumber("Num Buckets", HashTable->nbucket); W.printNumber("Num Chains", HashTable->nchain); @@ -4112,7 +4136,7 @@ template void GNUStyle::printHashSymbols() { if (const Elf_Hash *SysVHash = this->dumper().getHashTable()) { OS << "\n Symbol table of .hash for image:\n"; - if (Error E = checkHashTable(this->Obj, SysVHash)) + if (Error E = checkHashTable(this->dumper(), SysVHash)) this->reportUniqueWarning(std::move(E)); else printHashTableSymbols(*SysVHash); @@ -4742,7 +4766,7 @@ template void GNUStyle::printHashHistograms() { // Print histogram for the .hash section. if (const Elf_Hash *HashTable = this->dumper().getHashTable()) { - if (Error E = checkHashTable(this->Obj, HashTable)) + if (Error E = checkHashTable(this->dumper(), HashTable)) this->reportUniqueWarning(std::move(E)); else printHashHistogram(*HashTable);