[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