[llvm-readelf] - Allow dumping dynamic symbols when there is no program headers.
authorGeorge Rimar <grimar@accesssoftek.com>
Thu, 5 Sep 2019 14:02:58 +0000 (14:02 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Thu, 5 Sep 2019 14:02:58 +0000 (14:02 +0000)
D62179 introduced a regression. llvm-readelf lose the ability to dump the dynamic symbols
when there is .dynamic section with a DT_SYMTAB, but there are no program headers:
https://reviews.llvm.org/D62179#1652778

Below is a program flow before the D62179 change:

1) Find SHT_DYNSYM.
2) Find there is no PT_DYNAMIC => don't try to parse it.
3) Print dynamic symbols using information about them found on step (1).

And after the change it became:

1) Find SHT_DYNSYM.
2) Find there is no PT_DYNAMIC => find SHT_DYNAMIC.
3) Parse dynamic table, but fail to handle the DT_SYMTAB because of the absence of the PT_LOAD. Report the "Virtual address is not in any segment" error.

This patch fixes the issue. For doing this it checks that the value of DT_SYMTAB was
mapped to a segment. If not - it ignores it.

Differential revision: https://reviews.llvm.org/D67078

llvm-svn: 371071

llvm/test/tools/llvm-readobj/dyn-symbols.test
llvm/tools/llvm-readobj/ELFDumper.cpp

index 0a80e5c..bfcd821 100644 (file)
@@ -1,10 +1,11 @@
-RUN: llvm-readobj --dyn-symbols %p/Inputs/dynamic-table-so.x86 | FileCheck %s
+RUN: llvm-readobj --dyn-symbols %p/Inputs/dynamic-table-so.x86 | FileCheck %s
 
-# Check the two-letter alias --dt is equivalent to the --dyn-symbols full flag
-# name.
-RUN: llvm-readobj --dt %p/Inputs/dynamic-table-so.x86 > %t.readobj-dt-alias
-RUN: llvm-readobj --dyn-symbols %p/Inputs/dynamic-table-so.x86 > %t.readobj-dt-no-alias
-RUN: diff %t.readobj-dt-alias %t.readobj-dt-no-alias
+## Check the two-letter alias --dt is equivalent to the --dyn-symbols full flag
+## name.
+
+# RUN: llvm-readobj --dt %p/Inputs/dynamic-table-so.x86 > %t.readobj-dt-alias
+# RUN: llvm-readobj --dyn-symbols %p/Inputs/dynamic-table-so.x86 > %t.readobj-dt-no-alias
+# RUN: diff %t.readobj-dt-alias %t.readobj-dt-no-alias
 
 # CHECK:      DynamicSymbols [
 # CHECK-NEXT:   Symbol {
@@ -161,3 +162,125 @@ RUN: diff %t.readobj-dt-alias %t.readobj-dt-no-alias
 # CHECK-NEXT:     Section: .fini
 # CHECK-NEXT:   }
 # CHECK-NEXT: ]
+
+## Check that we are able to dump the dynamic symbol table even when we have no program headers.
+## In this case we find the table by it's type (SHT_DYNSYM) and ignore the DT_SYMTAB value.
+
+# RUN: yaml2obj --docnum=1 %s -o %t1.so
+# RUN: llvm-readobj %t1.so --dyn-symbols | FileCheck %s --check-prefix=NOPHDRS
+
+# NOPHDRS: Name: foo
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_SYMTAB
+        Value: 0xffff1234
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name: foo
+
+## Check we report a warning when there is no SHT_DYNSYM section and we can't map the DT_SYMTAB value
+## to an address because of the absence of a corresponding PT_LOAD program header.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2.so
+# RUN: llvm-readobj %t2.so --dyn-symbols 2>&1 | FileCheck %s -DFILE=%t2.so --check-prefix=NOSHT-DYNSYM
+
+# NOSHT-DYNSYM: warning: '[[FILE]]': Unable to parse DT_SYMTAB: virtual address is not in any segment: 0x0
+# NOSHT-DYNSYM:      DynamicSymbols [
+# NOSHT-DYNSYM-NEXT: ]
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:    .dynsym
+    Type:    SHT_PROGBITS
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_SYMTAB
+        Value: 0
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name: foo
+
+## Check that when we can't map the value of the DT_SYMTAB tag to an address, we report a warning and
+## use the information in the section header table to locate the dynamic symbol table.
+
+# RUN: yaml2obj --docnum=3 %s -o %t3.so
+# RUN: llvm-readobj %t3.so --dyn-symbols 2>&1 | FileCheck -DFILE=%t3.so %s --check-prefix=BROKEN-DTSYMTAB
+
+# BROKEN-DTSYMTAB: warning: '[[FILE]]': Unable to parse DT_SYMTAB: virtual address is not in any segment: 0xffff1234
+# BROKEN-DTSYMTAB: Name: foo
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_SYMTAB
+        Value: 0xffff1234
+      - Tag:   DT_NULL
+        Value: 0
+DynamicSymbols:
+  - Name: foo
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x0000
+    Sections:
+      - Section: .dynsym
+
+## Check that if we can get the location of the dynamic symbol table using both the DT_SYMTAB value
+## and the section headers table then we prefer the former and report a warning.
+
+# RUN: yaml2obj --docnum=4 %s -o %t4.so
+# RUN: llvm-readobj %t4.so --dyn-symbols 2>&1 | FileCheck -DFILE=%t4.so %s --check-prefix=PREFER-DTSYMTAB
+
+# PREFER-DTSYMTAB: warning: '[[FILE]]': SHT_DYNSYM section header and DT_SYMTAB disagree about the location of the dynamic symbol table
+# PREFER-DTSYMTAB: Name: o
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_DYN
+  Machine: EM_X86_64
+Sections:
+  - Name:    .dynamic
+    Type:    SHT_DYNAMIC
+    Entries:
+      - Tag:   DT_SYMTAB
+        Value: 0x0
+      - Tag:   DT_NULL
+        Value: 0
+  - Name: .dynsym
+    Type: SHT_DYNSYM
+  - Name: .mydynsym
+    Type: SHT_DYNSYM
+## The Content describes 2 symbols: zero symbol and symbol with st_name == 3.
+    Content: "000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000"
+DynamicSymbols:
+  - Name: foo
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x0000
+    Sections:
+      - Section: .mydynsym
index b019ed8..bc17360 100644 (file)
@@ -1643,10 +1643,29 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
     case ELF::DT_STRSZ:
       StringTableSize = Dyn.getVal();
       break;
-    case ELF::DT_SYMTAB:
-      DynSymRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
-      DynSymRegion.EntSize = sizeof(Elf_Sym);
+    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 (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);
+      }
       break;
+    }
     case ELF::DT_RELA:
       DynRelaRegion.Addr = toMappedAddr(Dyn.getTag(), Dyn.getPtr());
       break;