[llvm] c587b07 - [llvm-readobj] - Add a validation of the GNU hash table to printGnuHashHistogram().
Georgii Rymar via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 19 04:35:36 PDT 2020
Author: Georgii Rymar
Date: 2020-06-19T14:24:51+03:00
New Revision: c587b076a0e56d6a70c3bb2b8f9d104a48edbfe4
URL: https://github.com/llvm/llvm-project/commit/c587b076a0e56d6a70c3bb2b8f9d104a48edbfe4
DIFF: https://github.com/llvm/llvm-project/commit/c587b076a0e56d6a70c3bb2b8f9d104a48edbfe4.diff
LOG: [llvm-readobj] - Add a validation of the GNU hash table to printGnuHashHistogram().
Similar to D81937, we might crash when printing a histogram for a GNU hash table
with a 'symndx' index that is larger than the number of dynamic symbols.
This patch adopts and reuses the `getGnuHashTableChains()` helper which performs
a validation of the table. As a side effect the warning reported for
the --gnu-hash-table was improved.
Also with this change we start to report a warning when the histogram is requested for
the GNU hash table, but the dynamic symbols table is empty (size == 0).
Differential revision: https://reviews.llvm.org/D82010
Added:
Modified:
llvm/test/tools/llvm-readobj/ELF/gnuhash.test
llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
llvm/tools/llvm-readobj/ELFDumper.cpp
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
index 84a29382b349..7fe2e465c648 100644
--- a/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
+++ b/llvm/test/tools/llvm-readobj/ELF/gnuhash.test
@@ -127,7 +127,7 @@ ProgramHeaders:
# SYMNDX-NEXT: Shift Count: 0
# SYMNDX-NEXT: Bloom Filter: [0x1]
# SYMNDX-NEXT: Buckets: [2]
-# SYMNDX-NEXT: warning: '[[FILE]]': the first hashed symbol index (2) is larger than the number of dynamic symbols (2)
+# SYMNDX-NEXT: warning: '[[FILE]]': unable to dump 'Values' for the SHT_GNU_HASH section: the first hashed symbol index (2) is larger than the number of dynamic symbols (2)
# SYMNDX-NEXT: }
--- !ELF
diff --git a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
index 6628891a1f04..525240e7dea7 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-histogram.test
@@ -318,3 +318,112 @@ ProgramHeaders:
Sections:
- Section: .gnu.hash
- Section: .dynamic
+
+## Linkers might produce an empty no-op SHT_GNU_HASH section when
+## there are no dynamic symbols or when all dynamic symbols are undefined.
+## Such sections normally have a single zero entry in the bloom
+## filter, a single zero entry in the hash bucket and no values.
+##
+## The index of the first symbol in the dynamic symbol table
+## included in the hash table can be set to the number of dynamic symbols,
+## which is one larger than the index of the last dynamic symbol.
+## For empty tables however, this value is unimportant and can be ignored.
+
+## Check the case when a 'symndx' index of the first symbol in the dynamic symbol
+## table is larger than the number of dynamic symbols.
+
+## Case A: when the buckets array is not empty and has a non-zero value we report a warning.
+# RUN: yaml2obj --docnum=7 -DVAL=0x1 %s -o %t9
+# RUN: llvm-readelf --elf-hash-histogram %t9 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t9 --check-prefix=ERR6
+
+# ERR6: warning: '[[FILE]]': unable to print the GNU hash table histogram: the first hashed symbol index (16) is larger than the number of dynamic symbols (1)
+
+## Case B: we do not report a warning when the buckets array contains only zero values.
+# RUN: yaml2obj --docnum=7 -DVAL=0x0 %s -o %t10
+# RUN: llvm-readelf --elf-hash-histogram %t10 2>&1 | \
+# RUN: FileCheck %s --allow-empty --implicit-check-not="Histogram"
+
+## Case C: we do not report a warning when the buckets array is empty.
+# RUN: yaml2obj --docnum=7 -DVAL="" %s -o %t11
+# RUN: llvm-readelf --elf-hash-histogram %t11 2>&1 | \
+# RUN: FileCheck %s --allow-empty --implicit-check-not="Histogram"
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .gnu.hash
+ Type: SHT_GNU_HASH
+ Flags: [ SHF_ALLOC ]
+ Header:
+ SymNdx: 0x10
+ Shift2: 0x0
+ BloomFilter: [ 0x0 ]
+ HashBuckets: [ [[VAL]] ]
+ HashValues: [ 0x0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_ALLOC ]
+ Link: .dynstr
+ Entries:
+ - Tag: DT_GNU_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+DynamicSymbols: []
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .gnu.hash
+ - Section: .dynamic
+
+## Check we report warnings when the dynamic symbol table is absent or empty.
+
+## The code locates the dynamic symbol table by the section type. Use SHT_PROGBITS to hide it.
+# RUN: yaml2obj --docnum=8 -DTYPE=SHT_PROGBITS %s -o %t12
+# RUN: llvm-readelf --elf-hash-histogram %t12 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t12 --check-prefix=ERR7
+
+# ERR7: warning: '[[FILE]]': unable to print the GNU hash table histogram: no dynamic symbol table found
+
+# RUN: yaml2obj --docnum=8 -DTYPE=SHT_DYNSYM %s -o %t13
+# RUN: llvm-readelf --elf-hash-histogram %t13 2>&1 | \
+# RUN: FileCheck %s -DFILE=%t13 --check-prefix=ERR8
+
+# ERR8: warning: '[[FILE]]': unable to print the GNU hash table histogram: the dynamic symbol table is empty
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_X86_64
+Sections:
+ - Name: .gnu.hash
+ Type: SHT_GNU_HASH
+ Flags: [ SHF_ALLOC ]
+ Header:
+ SymNdx: 0x0
+ Shift2: 0x0
+ BloomFilter: [ 0x0 ]
+ HashBuckets: [ 0x0 ]
+ HashValues: [ 0x0 ]
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Flags: [ SHF_ALLOC ]
+ Entries:
+ - Tag: DT_GNU_HASH
+ Value: 0x0
+ - Tag: DT_NULL
+ Value: 0x0
+ - Name: .dynsym
+ Type: [[TYPE]]
+ Size: 0
+ProgramHeaders:
+ - Type: PT_LOAD
+ Sections:
+ - Section: .gnu.hash
diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index 29776b162745..832e7ce9791c 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -325,6 +325,8 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
return Table.slice(0, Size);
}
+ Optional<DynRegionInfo> getDynSymRegion() const { return DynSymRegion; }
+
Elf_Sym_Range dynamic_symbols() const {
if (!DynSymRegion)
return Elf_Sym_Range();
@@ -2718,12 +2720,16 @@ template <typename ELFT> void ELFDumper<ELFT>::printHashTable() {
template <class ELFT>
static Expected<ArrayRef<typename ELFT::Word>>
-getGnuHashTableChains(const typename ELFT::SymRange DynSymTable,
+getGnuHashTableChains(Optional<DynRegionInfo> DynSymRegion,
const typename ELFT::GnuHash *GnuHashTable) {
+ if (!DynSymRegion)
+ return createError("no dynamic symbol table found");
+
+ ArrayRef<typename ELFT::Sym> DynSymTable =
+ DynSymRegion->getAsArrayRef<typename ELFT::Sym>();
size_t NumSyms = DynSymTable.size();
if (!NumSyms)
- return createError("unable to dump 'Values' for the SHT_GNU_HASH "
- "section: the dynamic symbol table is empty");
+ return createError("the dynamic symbol table is empty");
if (GnuHashTable->symndx < NumSyms)
return GnuHashTable->values(NumSyms);
@@ -2773,17 +2779,13 @@ void ELFDumper<ELFT>::printGnuHashTable(const object::ObjectFile *Obj) {
ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
W.printList("Buckets", Buckets);
- if (!DynSymRegion) {
- reportWarning(createError("unable to dump 'Values' for the SHT_GNU_HASH "
- "section: no dynamic symbol table found"),
- ObjF->getFileName());
- return;
- }
-
Expected<ArrayRef<Elf_Word>> Chains =
- getGnuHashTableChains<ELFT>(dynamic_symbols(), GnuHashTable);
+ getGnuHashTableChains<ELFT>(DynSymRegion, GnuHashTable);
if (!Chains) {
- reportUniqueWarning(Chains.takeError());
+ reportUniqueWarning(
+ createError("unable to dump 'Values' for the SHT_GNU_HASH "
+ "section: " +
+ toString(Chains.takeError())));
return;
}
@@ -4660,20 +4662,26 @@ void GNUStyle<ELFT>::printHashHistogram(const Elf_Hash &HashTable) {
template <class ELFT>
void GNUStyle<ELFT>::printGnuHashHistogram(const Elf_GnuHash &GnuHashTable) {
- size_t NBucket = GnuHashTable.nbuckets;
- ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
- unsigned NumSyms = this->dumper()->dynamic_symbols().size();
- if (!NumSyms)
+ Expected<ArrayRef<Elf_Word>> ChainsOrErr = getGnuHashTableChains<ELFT>(
+ this->dumper()->getDynSymRegion(), &GnuHashTable);
+ if (!ChainsOrErr) {
+ this->reportUniqueWarning(
+ createError("unable to print the GNU hash table histogram: " +
+ toString(ChainsOrErr.takeError())));
return;
- ArrayRef<Elf_Word> Chains = GnuHashTable.values(NumSyms);
+ }
+
+ ArrayRef<Elf_Word> Chains = *ChainsOrErr;
size_t Symndx = GnuHashTable.symndx;
size_t TotalSyms = 0;
size_t MaxChain = 1;
size_t CumulativeNonZero = 0;
+ size_t NBucket = GnuHashTable.nbuckets;
if (Chains.empty() || NBucket == 0)
return;
+ ArrayRef<Elf_Word> Buckets = GnuHashTable.buckets();
std::vector<size_t> ChainLen(NBucket, 0);
for (size_t B = 0; B < NBucket; B++) {
if (!Buckets[B])
More information about the llvm-commits
mailing list