[llvm] b259ce9 - [llvm-readobj] Derive dynamic symtab size from DT_HASH

James Henderson via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 23 05:21:42 PDT 2020


Author: James Henderson
Date: 2020-03-23T12:21:20Z
New Revision: b259ce998f5537fd4d8ac8418de643406344141d

URL: https://github.com/llvm/llvm-project/commit/b259ce998f5537fd4d8ac8418de643406344141d
DIFF: https://github.com/llvm/llvm-project/commit/b259ce998f5537fd4d8ac8418de643406344141d.diff

LOG: [llvm-readobj] Derive dynamic symtab size from DT_HASH

If the section headers have been removed by a tool such as llvm-objcopy
or llvm-strip, previously llvm-readobj/llvm-readelf would not dump the
dynamic symbols when --dyn-symbols was specified. However, the nchain
value of the DT_HASH data specifies the number of dynamic symbols, so if
it is present, we can use that. This patch implements this behaviour.

Fixes https://bugs.llvm.org/show_bug.cgi?id=45089.

Reviewed by: grimar, MaskRay

Differential Revision: https://reviews.llvm.org/D76352

Added: 
    llvm/test/tools/llvm-readobj/ELF/dyn-symbols-size-from-hash-table.test

Modified: 
    llvm/test/tools/llvm-readobj/ELF/all.test
    llvm/tools/llvm-readobj/ELFDumper.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-readobj/ELF/all.test b/llvm/test/tools/llvm-readobj/ELF/all.test
index 39783613e789..501c480f23df 100644
--- a/llvm/test/tools/llvm-readobj/ELF/all.test
+++ b/llvm/test/tools/llvm-readobj/ELF/all.test
@@ -112,4 +112,5 @@ ProgramHeaders:
     Sections:
       - Section: .note.gnu.build-id
 Symbols: []
-DynamicSymbols: []
+DynamicSymbols:
+  - Name: foo

diff  --git a/llvm/test/tools/llvm-readobj/ELF/dyn-symbols-size-from-hash-table.test b/llvm/test/tools/llvm-readobj/ELF/dyn-symbols-size-from-hash-table.test
new file mode 100644
index 000000000000..80cb8e3e9fa4
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/ELF/dyn-symbols-size-from-hash-table.test
@@ -0,0 +1,314 @@
+## This test shows how llvm-readobj uses the hash table section to derive the
+## size of a dynamic symbol table. This allows dumping of the dynamic symbol
+## table in the event of an object without section headers.
+
+## Case 1a) Table size is derived from hash table, with DT_SYMTAB before DT_HASH.
+# RUN: yaml2obj --docnum=1 %s -o %t1a-64 -DBITS=64 \
+# RUN:          -DTAG1=DT_SYMTAB -DTAG2=DT_HASH -DVAL1=0x400 -DVAL2=0x600
+# RUN: llvm-strip --strip-sections %t1a-64
+# RUN: llvm-readobj --dyn-symbols %t1a-64 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM1,STRIP --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t1a-64 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU1,GNU1-STRIP --implicit-check-not=warning:
+# RUN: yaml2obj --docnum=1 %s -o %t1a-32 -DBITS=32 \
+# RUN:          -DTAG1=DT_SYMTAB -DTAG2=DT_HASH -DVAL1=0x400 -DVAL2=0x600
+# RUN: llvm-strip --strip-sections %t1a-32
+# RUN: llvm-readobj --dyn-symbols %t1a-32 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM1,STRIP --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t1a-32 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU1,GNU1-STRIP --implicit-check-not=warning:
+
+## 1b) Table size is derived from hash table, with DT_HASH before DT_SYMTAB.
+## We don't bother testing 32 and 64-bit here. The above cases show that reading
+## the nchain value is correct for all formats, and other tests show the basic
+## printing behaviour.
+# RUN: yaml2obj --docnum=1 %s -o %t1b-64 -DBITS=64 \
+# RUN:          -DTAG1=DT_HASH -DTAG2=DT_SYMTAB -DVAL1=0x600 -DVAL2=0x400
+# RUN: llvm-strip --strip-sections %t1b-64
+# RUN: llvm-readobj --dyn-symbols %t1b-64 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM1,STRIP --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t1b-64 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU1,GNU1-STRIP --implicit-check-not=warning:
+
+# LLVM1:      DynamicSymbols [
+# LLVM1-NEXT:   Symbol {
+# LLVM1-NEXT:     Name:  (0)
+# LLVM1-NEXT:     Value: 0x0
+# LLVM1-NEXT:     Size: 0
+# LLVM1-NEXT:     Binding: Local (0x0)
+# LLVM1-NEXT:     Type: None (0x0)
+# LLVM1-NEXT:     Other: 0
+# LLVM1-NEXT:     Section: Undefined (0x0)
+# LLVM1-NEXT:   }
+# LLVM1-NEXT:   Symbol {
+# LLVM1-NEXT:     Name: foo (5)
+# LLVM1-NEXT:     Value: 0x100
+# LLVM1-NEXT:     Size: 0
+# LLVM1-NEXT:     Binding: Local (0x0)
+# LLVM1-NEXT:     Type: Function (0x2)
+# LLVM1-NEXT:     Other: 0
+# STRIP-NEXT:     Section: <?> (0x1)
+# NOSTRIP-NEXT:   Section: .text (0x1)
+# LLVM1-NEXT:   }
+# LLVM1-NEXT:   Symbol {
+# LLVM1-NEXT:     Name: bar (1)
+# LLVM1-NEXT:     Value: 0x200
+# LLVM1-NEXT:     Size: 0
+# LLVM1-NEXT:     Binding: Local (0x0)
+# LLVM1-NEXT:     Type: Object (0x1)
+# LLVM1-NEXT:     Other: 0
+# STRIP-NEXT:     Section: <?> (0x2)
+# NOSTRIP-NEXT:   Section: .data (0x2)
+# LLVM1-NEXT:   }
+# LLVM1-NEXT: ]
+
+# GNU1-STRIP:   Symbol table for image contains 3 entries:
+# GNU1-NOSTRIP: Symbol table '.dynsym' contains 3 entries:
+# GNU1-NEXT:      Num:    Value        Size Type    Bind   Vis       Ndx Name
+# GNU1-NEXT:        0: {{0*}}00000000     0 NOTYPE  LOCAL  DEFAULT   UND
+# GNU1-NEXT:        1: {{0*}}00000100     0 FUNC    LOCAL  DEFAULT     1 foo
+# GNU1-NEXT:        2: {{0*}}00000200     0 OBJECT  LOCAL  DEFAULT     2 bar
+# GNU1-EMPTY:
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS[[BITS]]
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:         .text
+    Type:         SHT_PROGBITS
+  - Name:         .data
+    Type:         SHT_PROGBITS
+  - Name:         .dynsym
+    Type:         SHT_DYNSYM
+    Flags:        [ SHF_ALLOC ]
+    Address:      0x400
+    AddressAlign: 0x400
+  - Name:         .hash
+    Type:         SHT_HASH
+    Flags:        [ SHF_ALLOC ]
+    Address:      0x600
+    AddressAlign: 0x200
+    Bucket:       [ 1 ]
+    Chain:        [ 1, 2, 3 ]
+  - Name:         .dynstr
+    Type:         SHT_STRTAB
+    Flags:        [ SHF_ALLOC ]
+    Address:      0x800
+    AddressAlign: 0x200
+  - Name:         .dynamic
+    Type:         SHT_DYNAMIC
+    Flags:        [ SHF_ALLOC ]
+    Address:      0xA00
+    AddressAlign: 0x200
+    Entries:
+      - Tag:   DT_STRTAB
+        Value: 0x800
+      - Tag:   DT_STRSZ
+        Value: 9
+      - Tag:   [[TAG1]]
+        Value: [[VAL1]]
+      - Tag:   [[TAG2]]
+        Value: [[VAL2]]
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name:    foo
+    Type:    STT_FUNC
+    Section: .text
+    Value:   0x100
+  - Name:    bar
+    Type:    STT_OBJECT
+    Section: .data
+    Value:   0x200
+ProgramHeaders:
+  - Type:  PT_LOAD
+    VAddr: 0
+    Sections:
+      - Section: .text
+      - Section: .data
+  - Type:  PT_LOAD
+    VAddr: 0x400
+    Sections:
+      - Section: .dynsym
+      - Section: .hash
+      - Section: .dynstr
+      - Section: .dynamic
+  - Type:  PT_DYNAMIC
+    VAddr: 0xA00
+    Sections:
+      - Section: .dynamic
+
+## Case 2: Table size from DT_HASH does not match size from section header.
+# RUN: yaml2obj --docnum=2 %s -o %t2-smaller -DCHAIN="[1, 2]"
+# RUN: llvm-readobj --dyn-symbols %t2-smaller 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM2,WARN \
+# RUN:              --implicit-check-not=warning: -DNCHAIN=2
+# RUN: llvm-readelf --dyn-symbols %t2-smaller 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU2,WARN \
+# RUN:                --implicit-check-not=warning: -DNCHAIN=2
+
+# RUN: yaml2obj --docnum=2 %s -o %t2-larger -DCHAIN="[1, 2, 3, 4]"
+# RUN: llvm-readobj --dyn-symbols %t2-larger 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM2,LLVM2-MORE,LLVM2-ALL,WARN \
+# RUN:                --implicit-check-not=warning: -DNCHAIN=4
+# RUN: llvm-readelf --dyn-symbols %t2-larger 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU2,GNU2-MORE,GNU2-ALL,WARN \
+# RUN:                --implicit-check-not=warning: -DNCHAIN=4
+
+## Show there's no warning if the sizes match
+# RUN: yaml2obj --docnum=2 %s -o %t2-same -DCHAIN="[1, 2, 3]"
+# RUN: llvm-readobj --dyn-symbols %t2-same 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM2,LLVM2-MORE --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t2-same 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU2,GNU2-MORE \
+# RUN:                --implicit-check-not=warning: -DNCHAIN=3
+
+# WARN: warning: '{{.*}}2-{{.*}}': hash table nchain ([[NCHAIN]]) 
diff ers from symbol count derived from SHT_DYNSYM section header (3)
+
+# LLVM2:           DynamicSymbols [
+# LLVM2-NEXT:        Symbol {
+# LLVM2-NEXT:          Name:  (0)
+# LLVM2-NEXT:          Value: 0x0
+# LLVM2-NEXT:          Size: 0
+# LLVM2-NEXT:          Binding: Local (0x0)
+# LLVM2-NEXT:          Type: None (0x0)
+# LLVM2-NEXT:          Other: 0
+# LLVM2-NEXT:          Section: Undefined (0x0)
+# LLVM2-NEXT:        }
+# LLVM2-NEXT:        Symbol {
+# LLVM2-NEXT:          Name: foo (9)
+# LLVM2-NEXT:          Value: 0x100
+# LLVM2-NEXT:          Size: 0
+# LLVM2-NEXT:          Binding: Local (0x0)
+# LLVM2-NEXT:          Type: Function (0x2)
+# LLVM2-NEXT:          Other: 0
+# LLVM2-NEXT:          Section: .text (0x1)
+# LLVM2-NEXT:        }
+# LLVM2-MORE-NEXT:   Symbol {
+# LLVM2-MORE-NEXT:     Name: bar (5)
+# LLVM2-MORE-NEXT:     Value: 0x200
+# LLVM2-MORE-NEXT:     Size: 0
+# LLVM2-MORE-NEXT:     Binding: Local (0x0)
+# LLVM2-MORE-NEXT:     Type: Object (0x1)
+# LLVM2-MORE-NEXT:     Other: 0
+# LLVM2-MORE-NEXT:     Section: .data (0x2)
+# LLVM2-MORE-NEXT:   }
+# LLVM2-ALL-NEXT:    Symbol {
+# LLVM2-ALL-NEXT:      Name: baz (1)
+# LLVM2-ALL-NEXT:      Value: 0x300
+# LLVM2-ALL-NEXT:      Size: 0
+# LLVM2-ALL-NEXT:      Binding: Local (0x0)
+# LLVM2-ALL-NEXT:      Type: Object (0x1)
+# LLVM2-ALL-NEXT:      Other: 0
+# LLVM2-ALL-NEXT:      Section: .data (0x2)
+# LLVM2-ALL-NEXT:    }
+# LLVM2-NEXT:      ]
+
+# GNU2:           Symbol table '.dynsym' contains [[NCHAIN]] entries:
+# GNU2-NEXT:        Num:    Value        Size Type    Bind   Vis       Ndx Name
+# GNU2-NEXT:          0: {{0*}}00000000     0 NOTYPE  LOCAL  DEFAULT   UND
+# GNU2-NEXT:          1: {{0*}}00000100     0 FUNC    LOCAL  DEFAULT     1 foo
+# GNU2-MORE-NEXT:     2: {{0*}}00000200     0 OBJECT  LOCAL  DEFAULT     2 bar
+# GNU2-ALL-NEXT:      3: {{0*}}00000300     0 OBJECT  LOCAL  DEFAULT     2 baz
+# GNU2-EMPTY:
+
+## In this YAML, we define 4 dynamic symbols (including the null symbol), but
+## constrain the .dynsym section header to say there are only 3. This means that
+## when a size of 4 is derived from the hash table, we still have a valid symbol
+## to dump.
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:         .text
+    Type:         SHT_PROGBITS
+  - Name:         .data
+    Type:         SHT_PROGBITS
+  - Name:         .dynsym
+    Type:         SHT_DYNSYM
+    Flags:        [ SHF_ALLOC ]
+    ShSize:       0x48
+    Address:      0x400
+    AddressAlign: 0x400
+  - Name:         .hash
+    Type:         SHT_HASH
+    Flags:        [ SHF_ALLOC ]
+    Address:      0x600
+    AddressAlign: 0x200
+    Bucket:       [ 1 ]
+    Chain:        [[CHAIN]]
+  - Name:         .dynstr
+    Type:         SHT_STRTAB
+    Flags:        [ SHF_ALLOC ]
+    Address:      0x800
+    AddressAlign: 0x200
+  - Name:         .dynamic
+    Type:         SHT_DYNAMIC
+    Flags:        [ SHF_ALLOC ]
+    Address:      0xA00
+    AddressAlign: 0x200
+    Entries:
+      - Tag:   DT_SYMTAB
+        Value: 0x400
+      - Tag:   DT_HASH
+        Value: 0x600
+      - Tag:   DT_STRTAB
+        Value: 0x800
+      - Tag:   DT_STRSZ
+        Value: 13
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name:    foo
+    Type:    STT_FUNC
+    Section: .text
+    Value:   0x100
+  - Name:    bar
+    Type:    STT_OBJECT
+    Section: .data
+    Value:   0x200
+  - Name:    baz
+    Type:    STT_OBJECT
+    Section: .data
+    Value:   0x300
+ProgramHeaders:
+  - Type:  PT_LOAD
+    VAddr: 0
+    Sections:
+      - Section: .text
+      - Section: .data
+  - Type:  PT_LOAD
+    VAddr: 0x400
+    Sections:
+      - Section: .dynsym
+      - Section: .hash
+      - Section: .dynstr
+      - Section: .dynamic
+  - Type:  PT_DYNAMIC
+    VAddr: 0xA00
+    Sections:
+      - Section: .dynamic
+
+## Case 3: DT_HASH is missing.
+## Show that no warning occurs if there are section headers.
+# RUN: yaml2obj --docnum=1 %s -o %t3 -DTAG1=DT_SYMTAB -DVAL1=0x400 -DTAG2=DT_NULL -DVAL2=0 -DBITS=64
+# RUN: llvm-readobj --dyn-symbols %t3 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=LLVM1,NOSTRIP --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t3 2>&1 | \
+# RUN:   FileCheck %s --check-prefixes=GNU1,GNU1-NOSTRIP --implicit-check-not=warning:
+
+## Show that size is treated as zero, if no section headers are present.
+# RUN: llvm-strip --strip-sections %t3
+# RUN: llvm-readobj --dyn-symbols %t3 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=LLVM3 --implicit-check-not=warning:
+# RUN: llvm-readelf --dyn-symbols %t3 2>&1 | \
+# RUN:   FileCheck %s --implicit-check-not={{.}} --allow-empty
+
+# LLVM3: DynamicSymbols [
+# LLVM3: ]

diff  --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp
index fe7ee7fa6048..defa4a3cc109 100644
--- a/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -283,7 +283,7 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   DynRegionInfo DynRelaRegion;
   DynRegionInfo DynRelrRegion;
   DynRegionInfo DynPLTRelRegion;
-  DynRegionInfo DynSymRegion;
+  Optional<DynRegionInfo> DynSymRegion;
   DynRegionInfo DynamicTable;
   StringRef DynamicStringTable;
   std::string SOName = "<Not found>";
@@ -322,7 +322,9 @@ template <typename ELFT> class ELFDumper : public ObjDumper {
   }
 
   Elf_Sym_Range dynamic_symbols() const {
-    return DynSymRegion.getAsArrayRef<Elf_Sym>();
+    if (!DynSymRegion)
+      return Elf_Sym_Range();
+    return DynSymRegion->getAsArrayRef<Elf_Sym>();
   }
 
   Elf_Rel_Range dyn_rels() const;
@@ -667,8 +669,8 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const {
     StrTable = DynamicStringTable;
     Syms = dynamic_symbols();
     SymtabName = DynSymtabName;
-    if (DynSymRegion.Addr)
-      Entries = DynSymRegion.Size / DynSymRegion.EntSize;
+    if (DynSymRegion)
+      Entries = DynSymRegion->Size / DynSymRegion->EntSize;
   } else {
     if (!DotSymtabSec)
       return;
@@ -993,7 +995,7 @@ std::error_code createELFDumper(const object::ObjectFile *Obj,
 
 template <class ELFT> Error ELFDumper<ELFT>::LoadVersionMap() const {
   // If there is no dynamic symtab or version table, there is nothing to do.
-  if (!DynSymRegion.Addr || !SymbolVersionSection)
+  if (!DynSymRegion || !SymbolVersionSection)
     return Error::success();
 
   // Has the VersionMap already been loaded?
@@ -1043,10 +1045,11 @@ Expected<StringRef> ELFDumper<ELFT>::getSymbolVersion(const Elf_Sym *Sym,
     return "";
   }
 
+  assert(DynSymRegion && "DynSymRegion has not been initialised");
   // Determine the position in the symbol table of this entry.
   size_t EntryIndex = (reinterpret_cast<uintptr_t>(Sym) -
-                        reinterpret_cast<uintptr_t>(DynSymRegion.Addr)) /
-                       sizeof(Elf_Sym);
+                       reinterpret_cast<uintptr_t>(DynSymRegion->Addr)) /
+                      sizeof(Elf_Sym);
 
   // Get the corresponding version index entry.
   const Elf_Versym *Versym = unwrapOrError(
@@ -1980,8 +1983,12 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
                            ScopedPrinter &Writer)
     : ObjDumper(Writer), ObjF(ObjF), DynRelRegion(ObjF->getFileName()),
       DynRelaRegion(ObjF->getFileName()), DynRelrRegion(ObjF->getFileName()),
-      DynPLTRelRegion(ObjF->getFileName()), DynSymRegion(ObjF->getFileName()),
-      DynamicTable(ObjF->getFileName()) {
+      DynPLTRelRegion(ObjF->getFileName()), DynamicTable(ObjF->getFileName()) {
+  if (opts::Output == opts::GNU)
+    ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
+  else
+    ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
+
   const ELFFile<ELFT> *Obj = ObjF->getELFFile();
   typename ELFT::ShdrRange Sections =
       unwrapOrError(ObjF->getFileName(), Obj->sections());
@@ -1992,9 +1999,9 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
         DotSymtabSec = &Sec;
       break;
     case ELF::SHT_DYNSYM:
-      if (!DynSymRegion.Size) {
+      if (!DynSymRegion) {
         DynSymRegion = createDRIFrom(&Sec);
-        DynSymRegion.Context =
+        DynSymRegion->Context =
             ("section with index " + Twine(&Sec - &Sections.front())).str();
         // This is only used (if Elf_Shdr present)for naming section in GNU
         // style
@@ -2034,11 +2041,6 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
   }
 
   loadDynamicTable(Obj);
-
-  if (opts::Output == opts::GNU)
-    ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
-  else
-    ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
 }
 
 template <typename ELFT>
@@ -2059,6 +2061,7 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
   uint64_t SONameOffset = 0;
   const char *StringTableBegin = nullptr;
   uint64_t StringTableSize = 0;
+  Optional<DynRegionInfo> DynSymFromTable;
   for (const Elf_Dyn &Dyn : dynamic_table()) {
     switch (Dyn.d_tag) {
     case ELF::DT_HASH:
@@ -2077,26 +2080,13 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
       StringTableSize = Dyn.getVal();
       break;
     case ELF::DT_SYMTAB: {
-      // Often we find the information about the dynamic symbol table
-      // location in the SHT_DYNSYM section header. However, the value in
-      // DT_SYMTAB has priority, because it is used by dynamic loaders to
-      // locate .dynsym at runtime. The location we find in the section header
-      // and the location we find here should match. If we can't map the
-      // DT_SYMTAB value to an address (e.g. when there are no program headers), we
-      // ignore its value.
+      // If we can't map the DT_SYMTAB value to an address (e.g. when there are
+      // no program headers), we ignore its value.
       if (const uint8_t *VA = toMappedAddr(Dyn.getTag(), Dyn.getPtr())) {
-        // EntSize is non-zero if the dynamic symbol table has been found via a
-        // section header.
-        if (DynSymRegion.EntSize && VA != DynSymRegion.Addr)
-          reportWarning(
-              createError(
-                  "SHT_DYNSYM section header and DT_SYMTAB disagree about "
-                  "the location of the dynamic symbol table"),
-              ObjF->getFileName());
-
-        DynSymRegion.Addr = VA;
-        DynSymRegion.EntSize = sizeof(Elf_Sym);
-        DynSymRegion.EntSizePrintName = "";
+        DynSymFromTable.emplace(ObjF->getFileName());
+        DynSymFromTable->Addr = VA;
+        DynSymFromTable->EntSize = sizeof(Elf_Sym);
+        DynSymFromTable->EntSizePrintName = "";
       }
       break;
     }
@@ -2176,6 +2166,48 @@ void ELFDumper<ELFT>::parseDynamicTable(const ELFFile<ELFT> *Obj) {
   if (StringTableBegin)
     DynamicStringTable = StringRef(StringTableBegin, StringTableSize);
   SOName = getDynamicString(SONameOffset);
+
+  if (DynSymRegion) {
+    // Often we find the information about the dynamic symbol table
+    // location in the SHT_DYNSYM section header. However, the value in
+    // DT_SYMTAB has priority, because it is used by dynamic loaders to
+    // locate .dynsym at runtime. The location we find in the section header
+    // and the location we find here should match.
+    if (DynSymFromTable && DynSymFromTable->Addr != DynSymRegion->Addr)
+      ELFDumperStyle->reportUniqueWarning(
+          createError("SHT_DYNSYM section header and DT_SYMTAB disagree about "
+                      "the location of the dynamic symbol table"));
+
+    // According to the ELF gABI: "The number of symbol table entries should
+    // 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 &&
+        HashTable->nchain != DynSymRegion->Size / DynSymRegion->EntSize)
+      ELFDumperStyle->reportUniqueWarning(createError(
+          "hash table nchain (" + Twine(HashTable->nchain) +
+          ") 
diff ers from symbol count derived from SHT_DYNSYM section "
+          "header (" +
+          Twine(DynSymRegion->Size / DynSymRegion->EntSize) + ")"));
+  }
+
+  // Delay the creation of the actual dynamic symbol table until now, so that
+  // checks can always be made against the section header-based properties,
+  // without worrying about tag order.
+  if (DynSymFromTable) {
+    if (!DynSymRegion) {
+      DynSymRegion = DynSymFromTable;
+    } else {
+      DynSymRegion->Addr = DynSymFromTable->Addr;
+      DynSymRegion->EntSize = DynSymFromTable->EntSize;
+      DynSymRegion->EntSizePrintName = DynSymFromTable->EntSizePrintName;
+    }
+  }
+
+  // Derive the dynamic symbol table size from the DT_HASH hash table, if
+  // present.
+  if (HashTable && DynSymRegion)
+    DynSymRegion->Size = HashTable->nchain * DynSymRegion->EntSize;
 }
 
 template <typename ELFT>
@@ -2591,7 +2623,7 @@ template <typename ELFT> void ELFDumper<ELFT>::printGnuHashTable() {
   ArrayRef<Elf_Word> Buckets = GnuHashTable->buckets();
   W.printList("Buckets", Buckets);
 
-  if (!DynSymRegion.Addr) {
+  if (!DynSymRegion) {
     reportWarning(createError("unable to dump 'Values' for the SHT_GNU_HASH "
                               "section: no dynamic symbol table found"),
                   ObjF->getFileName());
@@ -3689,10 +3721,10 @@ void GNUStyle<ELFT>::printSymtabMessage(const ELFO *Obj, StringRef Name,
                                         size_t Entries,
                                         bool NonVisibilityBitsUsed) {
   if (!Name.empty())
-    OS << "\nSymbol table '" << Name << "' contains " << Entries
-       << " entries:\n";
+    OS << "\nSymbol table '" << Name << "'";
   else
-    OS << "\n Symbol table for image:\n";
+    OS << "\nSymbol table for image";
+  OS << " contains " << Entries << " entries:\n";
 
   if (ELFT::Is64Bits)
     OS << "   Num:    Value          Size Type    Bind   Vis";
@@ -5885,7 +5917,12 @@ void LLVMStyle<ELFT>::printSymbolSection(const Elf_Sym *Symbol,
   Expected<StringRef> SectionName =
       this->dumper()->getSymbolSectionName(Symbol, *SectionIndex);
   if (!SectionName) {
-    this->reportUniqueWarning(SectionName.takeError());
+    // Don't report an invalid section name if the section headers are missing.
+    // In such situations, all sections will be "invalid".
+    if (!this->dumper()->getElfObject()->sections().empty())
+      this->reportUniqueWarning(SectionName.takeError());
+    else
+      consumeError(SectionName.takeError());
     W.printHex("Section", "<?>", *SectionIndex);
   } else {
     W.printHex("Section", *SectionName, *SectionIndex);


        


More information about the llvm-commits mailing list