[llvm-readobj/llvm-objdump] - Improve how tool locate the dynamic table and report...
authorGeorge Rimar <grimar@accesssoftek.com>
Tue, 17 Sep 2019 13:58:46 +0000 (13:58 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Tue, 17 Sep 2019 13:58:46 +0000 (13:58 +0000)
Before this patch we gave a priority to a dynamic table found
from the section header.

It was discussed (here: https://reviews.llvm.org/D67078?id=218356#inline-602082)
that probably preferring the table from PT_DYNAMIC is better,
because it is what runtime loader sees.

This patch makes the table from PT_DYNAMIC be chosen at first place if it is available.
But also it adds logic to fall back to SHT_DYNAMIC if the table from the dynamic segment is
broken or fall back to use no table if both are broken.

It adds a few more diagnostic warnings for the logic above.

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

llvm-svn: 372122

llvm/test/tools/llvm-readobj/elf-dynamic-malformed.test
llvm/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test
llvm/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test
llvm/tools/llvm-readobj/ELFDumper.cpp

index b377eae..afdcbe6 100644 (file)
@@ -7,26 +7,21 @@
 # RUN: llvm-readelf --all %t.bad-size 2>&1 \
 # RUN:   | FileCheck %s -DFILE=%t.bad-size --implicit-check-not=warning --check-prefix WARN-GNU
 
-# WARN-NOT: warning
 # WARN: warning: '[[FILE]]': invalid section size (4) or entity size (16)
+# WARN: warning: '[[FILE]]': invalid section size (4) or entity size (16)
+# WARN: warning: '[[FILE]]': no valid dynamic table was found
 # WARN-EMPTY:
-# WARN-NEXT: File:
-# WARN:      Symbols [
-# WARN:      ]
-# WARN-EMPTY:
-## A warning is printed at the place where a normal dynamic table should be. 
-# WARN-NEXT: warning: '[[FILE]]': invalid section size (4) or entity size (16)
-# WARN-NEXT: ProgramHeaders [
-
-# WARN-GNU-NOT:  warning
-# WARN-GNU:      warning: '[[FILE]]': invalid section size (4) or entity size (16)
+# WARN: File:
+# WARN: Symbols [
+# WARN: ]
+# WARN: ProgramHeaders [
+
+# WARN-GNU: warning: '[[FILE]]': invalid section size (4) or entity size (16)
+# WARN-GNU: warning: '[[FILE]]': invalid section size (4) or entity size (16)
+# WARN-GNU: warning: '[[FILE]]': no valid dynamic table was found
 # WARN-GNU-NEXT: ELF Header:
 # WARN-GNU:      Symbol table '.symtab' contains 1 entries:
 # WARN-GNU:        0:
-# WARN-GNU-EMPTY:
-## A warning is printed at the place where a normal dynamic table should be. 
-# WARN-GNU:      warning: '[[FILE]]': invalid section size (4) or entity size (16)
-# WARN-GNU-EMPTY:
 
 --- !ELF
 FileHeader:
index 163e610..cb8da63 100644 (file)
@@ -1,24 +1,86 @@
-## Show that llvm-readobj/llvm-readelf tools can dump the .dynamic
-## section when it is not in a PT_DYNAMIC segment.
+## Show that llvm-readobj/llvm-readelf tools sometimes can dump the
+## dynamic table when it is not in a PT_DYNAMIC segment.
 
-# RUN: yaml2obj %s -o %t.o
-# RUN: llvm-readobj --dynamic-table %t.o 2>&1 \
-# RUN:   | FileCheck -DFILE=%t.o --check-prefixes=WARNING,LLVM %s
-# RUN: llvm-readelf --dynamic-table %t.o 2>&1 \
-# RUN:   | FileCheck -DFILE=%t.o --check-prefixes=WARNING,GNU %s
+## Case 1: The dynamic table found using the dynamic program header is corrupted
+##         (<size of data> % <size of dynamic entry> != 0). So the table is taken
+##         from the section header.
 
-# WARNING:   warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment
+# RUN: yaml2obj --docnum=1 %s -o %t1.o
+# RUN: llvm-readobj --dynamic-table %t1.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t1.o --check-prefixes=WARNING1,LLVM1 %s
+# RUN: llvm-readelf --dynamic-table %t1.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t1.o --check-prefixes=WARNING1,GNU1 %s
 
-# LLVM:      DynamicSection [ (2 entries)
-# LLVM-NEXT:   Tag                Type     Name/Value
-# LLVM-NEXT:   0x0000000000000018 BIND_NOW 0x1
-# LLVM-NEXT:   0x0000000000000000 NULL     0x0
-# LLVM-NEXT: ]
+# WARNING1: warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment
+# WARNING1: warning: '[[FILE]]': invalid section size (1) or entity size (16)
+# WARNING1: warning: '[[FILE]]': SHT_DYNAMIC section header and PT_DYNAMIC program header disagree about the location of the dynamic table
+# WARNING1: warning: '[[FILE]]': PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used
 
-# GNU:      Dynamic section at offset 0x{{.*}} contains 2 entries:
-# GNU-NEXT:   Tag                Type       Name/Value
-# GNU-NEXT:   0x0000000000000018 (BIND_NOW) 0x1
-# GNU-NEXT:   0x0000000000000000 (NULL)     0x0
+# LLVM1:      DynamicSection [ (2 entries)
+# LLVM1-NEXT:   Tag                Type     Name/Value
+# LLVM1-NEXT:   0x0000000000000018 BIND_NOW 0x1
+# LLVM1-NEXT:   0x0000000000000000 NULL     0x0
+# LLVM1-NEXT: ]
+
+# GNU1:      Dynamic section at offset 0x{{.*}} contains 2 entries:
+# GNU1-NEXT:   Tag                Type       Name/Value
+# GNU1-NEXT:   0x0000000000000018 (BIND_NOW) 0x1
+# GNU1-NEXT:   0x0000000000000000 (NULL)     0x0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Flags: [SHF_ALLOC]
+    Address: 0x1000
+    AddressAlign: 0x1000
+    Entries:
+      - Tag:   DT_BIND_NOW
+        Value: 0x1
+      - Tag:   DT_NULL
+        Value: 0x0
+  - Name: .text
+    Type: SHT_PROGBITS
+    Flags: [SHF_ALLOC]
+    Address: 0x1100
+    AddressAlign: 0x100
+    Content: "00"
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x1000
+    Sections:
+      - Section: .dynamic
+      - Section: .text
+  - Type: PT_DYNAMIC
+    VAddr: 0x1000
+    Sections:
+      - Section: .text
+
+## Case 2: The dynamic table found using the dynamic program header is different from the
+##         table found using the section header table.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2.o
+# RUN: llvm-readobj --dynamic-table %t2.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t2.o --check-prefixes=WARNING2,LLVM2 %s
+# RUN: llvm-readelf --dynamic-table %t2.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t2.o --check-prefixes=WARNING2,GNU2 %s
+
+# WARNING2: warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment
+# WARNING2: warning: '[[FILE]]': SHT_DYNAMIC section header and PT_DYNAMIC program header disagree about the location of the dynamic table
+
+# LLVM2:      DynamicSection [ (1 entries)
+# LLVM2-NEXT:   Tag                Type     Name/Value
+# LLVM2-NEXT:   0x0000000000000000 NULL     0x0
+# LLVM2-NEXT: ]
+
+# GNU2:      Dynamic section at offset 0x{{.*}} contains 1 entries:
+# GNU2-NEXT:   Tag                Type       Name/Value
+# GNU2-NEXT:   0x0000000000000000 (NULL)     0x0
 
 --- !ELF
 FileHeader:
@@ -42,6 +104,48 @@ Sections:
     Flags: [SHF_ALLOC]
     Address: 0x1100
     AddressAlign: 0x100
+    Content: "00000000000000000000000000000000"
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x1000
+    Sections:
+      - Section: .dynamic
+      - Section: .text
+  - Type: PT_DYNAMIC
+    VAddr: 0x1000
+    Sections:
+      - Section: .text
+
+## Case 3: Both dynamic tables found using SHT_DYNAMIC/PT_DYNAMIC are corrupted.
+
+# RUN: yaml2obj --docnum=3 %s -o %t3.o
+# RUN: llvm-readobj --dynamic-table %t3.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t3.o --check-prefix=WARNING3 --implicit-check-not="Dynamic" %s
+# RUN: llvm-readelf --dynamic-table %t3.o 2>&1 \
+# RUN:   | FileCheck -DFILE=%t3.o --check-prefix=WARNING3 --implicit-check-not="Dynamic" %s
+
+# WARNING3: warning: '[[FILE]]': invalid section size (1) or entity size (16)
+# WARNING3: warning: '[[FILE]]': SHT_DYNAMIC section header and PT_DYNAMIC program header disagree about the location of the dynamic table
+# WARNING3: warning: '[[FILE]]': no valid dynamic table was found
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Flags: [SHF_ALLOC]
+    Address: 0x1000
+    AddressAlign: 0x1000
+    Content: "00"
+  - Name: .text
+    Type: SHT_PROGBITS
+    Flags: [SHF_ALLOC]
+    Address: 0x1100
+    AddressAlign: 0x100
     Content: "00"
 ProgramHeaders:
   - Type: PT_LOAD
index 08bae94..77dea95 100644 (file)
@@ -4,13 +4,16 @@
 ## In the first case .text is placed before .dynamic.
 ## We check that we warn about this case.
 
-# RUN: yaml2obj --docnum=1 %s -o %t.o
-# RUN: llvm-readobj --dynamic-table %t.o 2>&1 \
-# RUN:   | FileCheck %s --DFILE=%t.o --check-prefixes=WARNING,LLVM
-# RUN: llvm-readelf --dynamic-table %t.o 2>&1 \
-# RUN:   | FileCheck %s --DFILE=%t.o --check-prefixes=WARNING,GNU
+# RUN: yaml2obj --docnum=1 %s -o %t1.o
+# RUN: llvm-readobj --dynamic-table %t1.o 2>&1 \
+# RUN:   | FileCheck %s --DFILE=%t1.o --check-prefixes=WARNING,LLVM
+# RUN: llvm-readelf --dynamic-table %t1.o 2>&1 \
+# RUN:   | FileCheck %s --DFILE=%t1.o --check-prefixes=WARNING,GNU
 
-# WARNING:    warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not at the start of PT_DYNAMIC segment
+# WARNING: warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not at the start of PT_DYNAMIC segment
+# WARNING: warning: '[[FILE]]': invalid section size (33) or entity size (16)
+# WARNING: warning: '[[FILE]]': SHT_DYNAMIC section header and PT_DYNAMIC program header disagree about the location of the dynamic table
+# WARNING: warning: '[[FILE]]': PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used
 
 # LLVM:      DynamicSection [ (2 entries)
 # LLVM-NEXT:   Tag                Type     Name/Value
@@ -39,8 +42,7 @@ Sections:
   - Name: .dynamic
     Type: SHT_DYNAMIC
     Flags: [SHF_ALLOC]
-    Address: 0x1100
-    AddressAlign: 0x1000
+    Address: 0x1001
     Entries:
       - Tag:   DT_BIND_NOW
         Value: 0x1
@@ -58,11 +60,81 @@ ProgramHeaders:
       - Section: .text
       - Section: .dynamic
 
-## In the second case .text goes after .dynamic and we don't display any warnings.
+## In this case .text goes after .dynamic and we don't display any warnings,
+## though the content of the .text is used for dumping the dynamic table.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2.o
+# RUN: llvm-readobj --dynamic-table %t2.o 2>&1 | FileCheck %s --check-prefix=LLVM2
+# RUN: llvm-readelf --dynamic-table %t2.o 2>&1 | FileCheck %s --check-prefix=GNU2
+
+# LLVM2:      DynamicSection [ (3 entries)
+# LLVM2-NEXT:   Tag                Type     Name/Value
+# LLVM2-NEXT:   0x0000000000000018 BIND_NOW 0x1
+# LLVM2-NEXT:   0x0000000000000018 BIND_NOW 0x2
+# LLVM2-NEXT:   0x0000000000000000 NULL     0x0
+# LLVM2-NEXT: ]
+
+# GNU2:      Dynamic section at offset 0x{{.*}} contains 3 entries:
+# GNU2-NEXT:   Tag                Type       Name/Value
+# GNU2-NEXT:   0x0000000000000018 (BIND_NOW) 0x1
+# GNU2-NEXT:   0x0000000000000018 (BIND_NOW) 0x2
+# GNU2-NEXT:   0x0000000000000000 (NULL)     0x0
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Flags: [SHF_ALLOC]
+    Address: 0x1000
+    AddressAlign: 0x1000
+    Entries:
+      - Tag:   DT_BIND_NOW
+        Value: 0x1
+      - Tag:   DT_BIND_NOW
+        Value: 0x2
+  - Name: .text
+    Type: SHT_PROGBITS
+    Flags: [SHF_ALLOC]
+    Address: 0x1100
+    AddressAlign: 0x100
+    Content: "00000000000000000000000000000000"
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x1000
+    Sections:
+      - Section: .dynamic
+      - Section: .text
+  - Type: PT_DYNAMIC
+    VAddr: 0x1000
+    Sections:
+      - Section: .dynamic
+      - Section: .text
+
+## In this case .text goes after .dynamic, but (PT_DYNAMIC segment size % dynamic entry size != 0)
+## and we have to use the information from the section header instead.
+
+# RUN: yaml2obj --docnum=3 %s -o %t3.o
+# RUN: llvm-readobj --dynamic-table %t3.o 2>&1 | FileCheck %s --DFILE=%t3.o --check-prefixes=WARNING2,LLVM3
+# RUN: llvm-readelf --dynamic-table %t3.o 2>&1 | FileCheck %s --DFILE=%t3.o --check-prefixes=WARNING2,GNU3
+
+# WARNING2: warning: '[[FILE]]': invalid section size (257) or entity size (16)
+# WARNING2: warning: '[[FILE]]': PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used
+
+# LLVM3:      DynamicSection [ (2 entries)
+# LLVM3-NEXT:   Tag                Type     Name/Value
+# LLVM3-NEXT:   0x0000000000000018 BIND_NOW 0x1
+# LLVM3-NEXT:   0x0000000000000000 NULL     0x0
+# LLVM3-NEXT: ]
 
-# RUN: yaml2obj --docnum=2 %s -o %t.o
-# RUN: llvm-readobj --dynamic-table %t.o | FileCheck %s --implicit-check-not="warning"
-# RUN: llvm-readelf --dynamic-table %t.o | FileCheck %s --implicit-check-not="warning"
+# GNU3:      Dynamic section at offset 0x{{.*}} contains 2 entries:
+# GNU3-NEXT:   Tag                Type       Name/Value
+# GNU3-NEXT:   0x0000000000000018 (BIND_NOW) 0x1
+# GNU3-NEXT:   0x0000000000000000 (NULL)     0x0
 
 --- !ELF
 FileHeader:
index 7876f41..51e242f 100644 (file)
@@ -218,6 +218,8 @@ private:
                      S->sh_entsize, ObjF->getFileName()});
   }
 
+  std::pair<const Elf_Phdr *, const Elf_Shdr *>
+  findDynamic(const ELFFile<ELFT> *Obj);
   void loadDynamicTable(const ELFFile<ELFT> *Obj);
   void parseDynamicTable();
 
@@ -1417,7 +1419,8 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) {
 }
 
 template <typename ELFT>
-void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
+std::pair<const typename ELFT::Phdr *, const typename ELFT::Shdr *>
+ELFDumper<ELFT>::findDynamic(const ELFFile<ELFT> *Obj) {
   // Try to locate the PT_DYNAMIC header.
   const Elf_Phdr *DynamicPhdr = nullptr;
   for (const Elf_Phdr &Phdr :
@@ -1438,53 +1441,112 @@ void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
     break;
   }
 
-  // Information in the section header has priority over the information
-  // in a PT_DYNAMIC header.
+  if (DynamicPhdr && DynamicPhdr->p_offset + DynamicPhdr->p_filesz >
+                         ObjF->getMemoryBufferRef().getBufferSize()) {
+    reportWarning(
+        createError(
+            "PT_DYNAMIC segment offset + size exceeds the size of the file"),
+        ObjF->getFileName());
+    // Don't use the broken dynamic header.
+    DynamicPhdr = nullptr;
+  }
+
+  if (DynamicPhdr && DynamicSec) {
+    StringRef Name =
+        unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DynamicSec));
+    if (DynamicSec->sh_addr + DynamicSec->sh_size >
+            DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz ||
+        DynamicSec->sh_addr < DynamicPhdr->p_vaddr)
+      reportWarning(createError("The SHT_DYNAMIC section '" + Name +
+                                "' is not contained within the "
+                                "PT_DYNAMIC segment"),
+                    ObjF->getFileName());
+
+    if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr)
+      reportWarning(createError("The SHT_DYNAMIC section '" + Name +
+                                "' is not at the start of "
+                                "PT_DYNAMIC segment"),
+                    ObjF->getFileName());
+  }
+
+  return std::make_pair(DynamicPhdr, DynamicSec);
+}
+
+template <typename ELFT>
+void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
+  const Elf_Phdr *DynamicPhdr;
+  const Elf_Shdr *DynamicSec;
+  std::tie(DynamicPhdr, DynamicSec) = findDynamic(Obj);
+  if (!DynamicPhdr && !DynamicSec)
+    return;
+
+  DynRegionInfo FromPhdr(ObjF->getFileName());
+  bool IsPhdrTableValid = false;
+  if (DynamicPhdr) {
+    FromPhdr = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn));
+    IsPhdrTableValid = !FromPhdr.getAsArrayRef<Elf_Dyn>().empty();
+  }
+
+  // Locate the dynamic table described in a section header.
   // Ignore sh_entsize and use the expected value for entry size explicitly.
-  // This allows us to dump the dynamic sections with a broken sh_entsize
+  // This allows us to dump dynamic sections with a broken sh_entsize
   // field.
+  DynRegionInfo FromSec(ObjF->getFileName());
+  bool IsSecTableValid = false;
   if (DynamicSec) {
-    DynamicTable =
+    FromSec =
         checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset,
                   DynamicSec->sh_size, sizeof(Elf_Dyn), ObjF->getFileName()});
-    parseDynamicTable();
+    IsSecTableValid = !FromSec.getAsArrayRef<Elf_Dyn>().empty();
   }
 
-  // If we have a PT_DYNAMIC header, we will either check the found dynamic
-  // section or take the dynamic table data directly from the header.
-  if (!DynamicPhdr)
-    return;
-
-  if (DynamicPhdr->p_offset + DynamicPhdr->p_filesz >
-      ObjF->getMemoryBufferRef().getBufferSize()) {
-    reportWarning(
-        createError(
-            "PT_DYNAMIC segment offset + size exceeds the size of the file"),
-        ObjF->getFileName());
+  // When we only have information from one of the SHT_DYNAMIC section header or
+  // PT_DYNAMIC program header, just use that.
+  if (!DynamicPhdr || !DynamicSec) {
+    if ((DynamicPhdr && IsPhdrTableValid) || (DynamicSec && IsSecTableValid)) {
+      DynamicTable = DynamicPhdr ? FromPhdr : FromSec;
+      parseDynamicTable();
+    } else {
+      reportWarning(createError("no valid dynamic table was found"),
+                    ObjF->getFileName());
+    }
     return;
   }
 
-  if (!DynamicSec) {
-    DynamicTable = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn));
-    parseDynamicTable();
-    return;
-  }
+  // At this point we have tables found from the section header and from the
+  // dynamic segment. Usually they match, but we have to do sanity checks to
+  // verify that.
 
-  StringRef Name =
-      unwrapOrError(ObjF->getFileName(), Obj->getSectionName(DynamicSec));
-  if (DynamicSec->sh_addr + DynamicSec->sh_size >
-          DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz ||
-      DynamicSec->sh_addr < DynamicPhdr->p_vaddr)
-    reportWarning(createError("The SHT_DYNAMIC section '" + Name +
-                              "' is not contained within the "
-                              "PT_DYNAMIC segment"),
+  if (FromPhdr.Addr != FromSec.Addr)
+    reportWarning(createError("SHT_DYNAMIC section header and PT_DYNAMIC "
+                              "program header disagree about "
+                              "the location of the dynamic table"),
                   ObjF->getFileName());
 
-  if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr)
-    reportWarning(createError("The SHT_DYNAMIC section '" + Name +
-                              "' is not at the start of "
-                              "PT_DYNAMIC segment"),
+  if (!IsPhdrTableValid && !IsSecTableValid) {
+    reportWarning(createError("no valid dynamic table was found"),
                   ObjF->getFileName());
+    return;
+  }
+
+  // Information in the PT_DYNAMIC program header has priority over the information
+  // in a section header.
+  if (IsPhdrTableValid) {
+    if (!IsSecTableValid)
+      reportWarning(
+          createError(
+              "SHT_DYNAMIC dynamic table is invalid: PT_DYNAMIC will be used"),
+          ObjF->getFileName());
+    DynamicTable = FromPhdr;
+  } else {
+    reportWarning(
+        createError(
+            "PT_DYNAMIC dynamic table is invalid: SHT_DYNAMIC will be used"),
+        ObjF->getFileName());
+    DynamicTable = FromSec;
+  }
+
+  parseDynamicTable();
 }
 
 template <typename ELFT>