]> granicus.if.org Git - llvm/commitdiff
[llvm-readelf] - Rework how we parse the .dynamic section.
authorGeorge Rimar <grimar@accesssoftek.com>
Mon, 20 May 2019 15:41:48 +0000 (15:41 +0000)
committerGeorge Rimar <grimar@accesssoftek.com>
Mon, 20 May 2019 15:41:48 +0000 (15:41 +0000)
This is a result of what I found during my work on https://bugs.llvm.org/show_bug.cgi?id=41679.

Previously LLVM readelf took the information about .dynamic section
from its PT_DYNAMIC segment only. GNU tools have a bit different logic.
They also use the information from the .dynamic section header if it is available.
This patch changes the code to improve the compatibility with the GNU Binutils.

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@361165 91177308-0d34-0410-b5e6-96231b3b80d8

test/Object/Inputs/corrupt-invalid-dynamic-table-size.elf.x86-64
test/Object/corrupt.test
test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test [new file with mode: 0644]
test/tools/llvm-readobj/elf-malformed-pt-dynamic.test
test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test [new file with mode: 0644]
tools/llvm-readobj/ELFDumper.cpp
tools/llvm-readobj/llvm-readobj.cpp
tools/llvm-readobj/llvm-readobj.h

index ee29a4162e391eeaa596d1c354556fe06657ed36..2eed83cfc370e1fa7374d450f30bb5f7a1098e24 100755 (executable)
Binary files a/test/Object/Inputs/corrupt-invalid-dynamic-table-size.elf.x86-64 and b/test/Object/Inputs/corrupt-invalid-dynamic-table-size.elf.x86-64 differ
index 35623b431b7bd7542b4e611c502819273468a5cb..c98757885aa120efc144c14e74c011cf5b80f879 100644 (file)
@@ -62,11 +62,11 @@ RUN: not llvm-readobj --dyn-relocations \
 RUN:   %p/Inputs/corrupt-invalid-dynamic-table-offset.elf.x86-64 2>&1 | \
 RUN:   FileCheck --check-prefix=DYN-TABLE-OFFSET %s
 
-DYN-TABLE-OFFSET: error: Invalid data was encountered while parsing the file
+DYN-TABLE-OFFSET: error: PT_DYNAMIC segment offset + size exceeds the size of the file
 
 
 RUN: not llvm-readobj --dyn-relocations \
 RUN:   %p/Inputs/corrupt-invalid-dynamic-table-too-large.elf.x86-64 2>&1 | \
 RUN:   FileCheck --check-prefix=DYN-TABLE-TOO-LARGE %s
 
-DYN-TABLE-TOO-LARGE: error: Invalid data was encountered while parsing the file
+DYN-TABLE-TOO-LARGE: error: PT_DYNAMIC segment offset + size exceeds the size of the file
diff --git a/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test b/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test
new file mode 100644 (file)
index 0000000..d52ec76
--- /dev/null
@@ -0,0 +1,47 @@
+## Show that llvm-readobj/llvm-readelf tools can dump the .dynamic
+## section when it is not in a PT_DYNAMIC segment.
+
+# RUN: yaml2obj %s -o %t.o
+# RUN: llvm-readobj --dynamic-table %t.o 2>&1 | FileCheck %s
+# RUN: llvm-readelf --dynamic-table %t.o 2>&1 | FileCheck %s
+
+# CHECK:      warning: The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment
+# CHECK:      DynamicSection [ (2 entries)
+# CHECK-NEXT:   Tag                Type     Name/Value
+# CHECK-NEXT:   0x0000000000000018 BIND_NOW 0x1
+# CHECK-NEXT:   0x0000000000000000 NULL     0x0
+# CHECK-NEXT: ]
+
+--- !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
index e762a27865fb731ed7a29629d32eb4be00322a28..db229b1b25900acaee7c27dd06ef118e5ad26068 100644 (file)
@@ -1,6 +1,5 @@
 # If the offset and/or size fields of the PT_DYNAMIC field become corrupted,
-# it will be impossible to read the dynamic segment validly. This test shows
-# that a sensible error message is given in this situation.
+# we should report a sensible error message.
 
 # Creating such a malformed file is hard. The easiest way to simulate it is to
 # truncate the file. Note that the section headers must first be stripped or
@@ -21,7 +20,7 @@
 # RUN: %python -c "with open(r'%t.truncated2', 'r+') as f: f.truncate(0xFFF)"
 # RUN: not llvm-readobj %t.truncated2 --dynamic-table 2>&1 | FileCheck %s
 
-# CHECK: error: Invalid data was encountered while parsing the file
+# CHECK: error: PT_DYNAMIC segment offset + size exceeds the size of the file
 
 --- !ELF
 FileHeader:
diff --git a/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test b/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test
new file mode 100644 (file)
index 0000000..610148f
--- /dev/null
@@ -0,0 +1,92 @@
+## Show that llvm-readobj/llvm-readelf tools can dump the .dynamic section which
+## is not alone in PT_DYNAMIC segment.
+
+## 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 | FileCheck %s --check-prefixes=WARNING,CHECK
+# RUN: llvm-readelf --dynamic-table %t.o 2>&1 | FileCheck %s --check-prefixes=WARNING,CHECK
+
+# WARNING:    warning: The SHT_DYNAMIC section '.dynamic' is not at the start of PT_DYNAMIC segment
+# CHECK:      DynamicSection [ (2 entries)
+# CHECK-NEXT:   Tag                Type     Name/Value
+# CHECK-NEXT:   0x0000000000000018 BIND_NOW 0x1
+# CHECK-NEXT:   0x0000000000000000 NULL     0x0
+# CHECK-NEXT: ]
+
+--- !ELF
+FileHeader:
+  Class:   ELFCLASS64
+  Data:    ELFDATA2LSB
+  Type:    ET_EXEC
+  Machine: EM_X86_64
+Sections:
+  - Name: .text
+    Type: SHT_PROGBITS
+    Flags: [SHF_ALLOC]
+    Address: 0x1000
+    AddressAlign: 0x100
+    Content: "00"
+  - Name: .dynamic
+    Type: SHT_DYNAMIC
+    Flags: [SHF_ALLOC]
+    Address: 0x1100
+    AddressAlign: 0x1000
+    Entries:
+      - Tag:   DT_BIND_NOW
+        Value: 0x1
+      - Tag:   DT_NULL
+        Value: 0x0
+ProgramHeaders:
+  - Type: PT_LOAD
+    VAddr: 0x1000
+    Sections:
+      - Section: .text
+      - Section: .dynamic
+  - Type: PT_DYNAMIC
+    VAddr: 0x1000
+    Sections:
+      - Section: .text
+      - Section: .dynamic
+
+## In the second case .text goes after .dynamic and we don't display any warnings.
+
+# 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"
+
+--- !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: .dynamic
+      - Section: .text
index 755798797628a7e150ebb10f2f5a91d7a6979d50..65abdbd6109f0b2c8fd26b42cab93f794da36ed3 100644 (file)
@@ -203,7 +203,8 @@ private:
         {ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize});
   }
 
-  void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments);
+  void loadDynamicTable(const ELFFile<ELFT> *Obj);
+  void parseDynamicTable();
 
   void printValue(uint64_t Type, uint64_t Value);
 
@@ -1329,21 +1330,72 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) {
 }
 
 template <typename ELFT>
-ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
-    ScopedPrinter &Writer)
-    : ObjDumper(Writer), ObjF(ObjF) {
-  SmallVector<const Elf_Phdr *, 4> LoadSegments;
-  const ELFFile<ELFT> *Obj = ObjF->getELFFile();
+void ELFDumper<ELFT>::loadDynamicTable(const ELFFile<ELFT> *Obj) {
+  const Elf_Phdr *DynamicPhdr = nullptr;
   for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) {
-    if (Phdr.p_type == ELF::PT_DYNAMIC) {
-      DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn));
+    if (Phdr.p_type != ELF::PT_DYNAMIC)
       continue;
-    }
-    if (Phdr.p_type != ELF::PT_LOAD || Phdr.p_filesz == 0)
+    DynamicPhdr = &Phdr;
+    break;
+  }
+
+  // We do not want to dump dynamic section if we have no PT_DYNAMIC header.
+  // This matches GNU's behavior.
+  if (!DynamicPhdr)
+    return;
+
+  // Try to locate the .dynamic section in the sections header table.
+  const Elf_Shdr *DynamicSec = nullptr;
+  for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
+    if (Sec.sh_type != ELF::SHT_DYNAMIC)
       continue;
-    LoadSegments.push_back(&Phdr);
+    DynamicSec = &Sec;
+    break;
   }
 
+  // Information in the section header has priority over the information
+  // in a PT_DYNAMIC 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
+  // field.
+  if (DynamicSec)
+    DynamicTable = checkDRI({ObjF->getELFFile()->base() + DynamicSec->sh_offset,
+                             DynamicSec->sh_size, sizeof(Elf_Dyn)});
+
+  if (DynamicPhdr->p_offset + DynamicPhdr->p_filesz >
+      ObjF->getMemoryBufferRef().getBufferSize())
+    reportError(
+        "PT_DYNAMIC segment offset + size exceeds the size of the file");
+
+  if (!DynamicSec) {
+    DynamicTable = createDRIFrom(DynamicPhdr, sizeof(Elf_Dyn));
+    parseDynamicTable();
+    return;
+  }
+
+  StringRef Name = unwrapOrError(Obj->getSectionName(DynamicSec));
+
+  if (DynamicSec->sh_addr + DynamicSec->sh_size >
+          DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz ||
+      DynamicSec->sh_addr < DynamicPhdr->p_vaddr)
+    reportWarning("The SHT_DYNAMIC section '" + Name +
+                  "' is not contained within the "
+                  "PT_DYNAMIC segment");
+
+  if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr)
+    reportWarning("The SHT_DYNAMIC section '" + Name +
+                  "' is not at the start of "
+                  "PT_DYNAMIC segment");
+
+  parseDynamicTable();
+}
+
+template <typename ELFT>
+ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
+    ScopedPrinter &Writer)
+    : ObjDumper(Writer), ObjF(ObjF) {
+  const ELFFile<ELFT> *Obj = ObjF->getELFFile();
+
   for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
     switch (Sec.sh_type) {
     case ELF::SHT_SYMTAB:
@@ -1390,7 +1442,7 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
     }
   }
 
-  parseDynamicTable(LoadSegments);
+  loadDynamicTable(Obj);
 
   if (opts::Output == opts::GNU)
     ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
@@ -1398,9 +1450,7 @@ ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF,
     ELFDumperStyle.reset(new LLVMStyle<ELFT>(Writer, this));
 }
 
-template <typename ELFT>
-void ELFDumper<ELFT>::parseDynamicTable(
-    ArrayRef<const Elf_Phdr *> LoadSegments) {
+template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable() {
   auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * {
     auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr);
     if (!MappedAddrOrError)
index e8cea5a7fffd10d76477569256d0f251023e82f6..1836e0fd0d7f090c5090904bb25c69a733cebc11 100644 (file)
@@ -373,6 +373,11 @@ LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) {
   exit(1);
 }
 
+void reportWarning(Twine Msg) {
+  errs() << "\n";
+  WithColor::warning(errs()) << Msg << "\n";
+}
+
 void error(Error EC) {
   if (!EC)
     return;
index 54318b2967694917cf251943770956d6f3d74002..c89871fbc7e1fc31a215cf9a5fe75bcc471618e1 100644 (file)
@@ -22,6 +22,7 @@ namespace llvm {
 
   // Various helper functions.
   LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg);
+  void reportWarning(Twine Msg);
   void error(std::error_code EC);
   void error(llvm::Error EC);
   template <typename T> T error(llvm::Expected<T> &&E) {