diff --git a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test --- a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test +++ b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test @@ -1,6 +1,6 @@ ## Check how the GNU Hash section is dumped with --gnu-hash-table. -# RUN: yaml2obj --docnum=1 %s -o %t.x64 +# RUN: yaml2obj --docnum=1 -D MASKWORDS=2 -D NBUCKETS=3 %s -o %t.x64 # RUN: yaml2obj --docnum=2 %s -o %t.x32 # RUN: llvm-readobj --gnu-hash-table %t.x64 | FileCheck %s @@ -32,6 +32,10 @@ Header: SymNdx: 0x1 Shift2: 0x2 +## The number of words in the Bloom filter. The value of 2 is no-op. + MaskWords: [[MASKWORDS]] +## The number of hash buckets. The value of 3 is no-op. + NBuckets: [[NBUCKETS]] BloomFilter: [0x3, 0x4] HashBuckets: [0x5, 0x6, 0x7] HashValues: [0x8, 0x9, 0xA, 0xB] @@ -314,3 +318,27 @@ Sections: - Section: .gnu.hash - Section: .dynamic + +## Check we report a proper warning when a hash table goes past the end of the file. + +## Case A: the 'nbuckets' field is set so that the table goes past the end of the file. +# RUN: yaml2obj --docnum=1 -D MASKWORDS=4294967295 -D NBUCKETS=3 %s -o %t.err.maskwords +# RUN: llvm-readobj --gnu-hash-table %t.err.maskwords 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.maskwords -DMASKWORDS=4294967295 -DNBUCKETS=3 --check-prefix=ERR +# RUN: llvm-readelf --gnu-hash-table %t.err.maskwords 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.maskwords -DMASKWORDS=4294967295 -DNBUCKETS=3 --check-prefix=ERR + +## Case B: the 'maskwords' field is set so that the table goes past the end of the file. +# RUN: yaml2obj --docnum=1 -D MASKWORDS=2 -D NBUCKETS=4294967295 %s -o %t.err.nbuckets +# RUN: llvm-readobj --gnu-hash-table %t.err.nbuckets 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR +# RUN: llvm-readelf --gnu-hash-table %t.err.nbuckets 2>&1 | \ +# RUN: FileCheck %s -DFILE=%t.err.nbuckets -DMASKWORDS=2 -DNBUCKETS=4294967295 --check-prefix=ERR + +# ERR: GnuHashTable { +# ERR-NEXT: Num Buckets: [[NBUCKETS]] +# ERR-NEXT: First Hashed Symbol Index: 1 +# ERR-NEXT: Num Mask Words: [[MASKWORDS]] +# ERR-NEXT: Shift Count: 2 +# ERR-NEXT: warning: '[[FILE]]': unable to dump the SHT_GNU_HASH section at 0x78: it goes past the end of the file +# ERR-NEXT: } 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 @@ -221,7 +221,7 @@ void printProgramHeaders(bool PrintProgramHeaders, cl::boolOrDefault PrintSectionMapping) override; void printHashTable() override; - void printGnuHashTable() override; + void printGnuHashTable(const object::ObjectFile *Obj) override; void printLoadName() override; void printVersionInfo() override; void printGroupSections() override; @@ -2666,7 +2666,8 @@ W.printList("Chains", HashTable->chains()); } -template void ELFDumper::printGnuHashTable() { +template +void ELFDumper::printGnuHashTable(const object::ObjectFile *Obj) { DictScope D(W, "GnuHashTable"); if (!GnuHashTable) return; @@ -2675,6 +2676,25 @@ W.printNumber("Num Mask Words", GnuHashTable->maskwords); W.printNumber("Shift Count", GnuHashTable->shift2); + MemoryBufferRef File = Obj->getMemoryBufferRef(); + const char *TableData = reinterpret_cast(GnuHashTable); + assert(TableData >= File.getBufferStart() && + TableData < File.getBufferEnd() && + "GnuHashTable must always point to a location inside the file"); + + uint64_t TableOffset = TableData - File.getBufferStart(); + if (TableOffset + + /*Header size:*/ 16 + GnuHashTable->nbuckets * 4 + + GnuHashTable->maskwords * sizeof(typename ELFT::Off) >= + File.getBufferSize()) { + reportWarning(createError("unable to dump the SHT_GNU_HASH " + "section at 0x" + + Twine::utohexstr(TableOffset) + + ": it goes past the end of the file"), + ObjF->getFileName()); + return; + } + ArrayRef BloomFilter = GnuHashTable->filter(); W.printHexList("Bloom Filter", BloomFilter); diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -59,7 +59,7 @@ virtual void printNeededLibraries() { } virtual void printSectionAsHex(StringRef SectionName) {} virtual void printHashTable() { } - virtual void printGnuHashTable() { } + virtual void printGnuHashTable(const object::ObjectFile *Obj) {} virtual void printHashSymbols() {} virtual void printLoadName() {} virtual void printVersionInfo() {} diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -489,7 +489,7 @@ if (opts::HashTable) Dumper->printHashTable(); if (opts::GnuHashTable) - Dumper->printGnuHashTable(); + Dumper->printGnuHashTable(Obj); if (opts::VersionInfo) Dumper->printVersionInfo(); if (Obj->isELF()) {