Index: llvm/trunk/test/tools/llvm-readobj/elf-dynamic-malformed.test =================================================================== --- llvm/trunk/test/tools/llvm-readobj/elf-dynamic-malformed.test +++ llvm/trunk/test/tools/llvm-readobj/elf-dynamic-malformed.test @@ -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: llvm/trunk/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test =================================================================== --- llvm/trunk/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test +++ llvm/trunk/test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test @@ -1,24 +1,31 @@ -## 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 - -# WARNING: warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not contained within the PT_DYNAMIC segment - -# LLVM: DynamicSection [ (2 entries) -# LLVM-NEXT: Tag Type Name/Value -# LLVM-NEXT: 0x0000000000000018 BIND_NOW 0x1 -# LLVM-NEXT: 0x0000000000000000 NULL 0x0 -# LLVM-NEXT: ] - -# 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 +## Case 1: The dynamic table found using the dynamic program header is corrupted +## ( % != 0). So the table is taken +## from the section header. + +# 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 + +# 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 + +# 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: @@ -53,3 +60,100 @@ 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: + 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: "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 + VAddr: 0x1000 + Sections: + - Section: .dynamic + - Section: .text + - Type: PT_DYNAMIC + VAddr: 0x1000 + Sections: + - Section: .text Index: llvm/trunk/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test =================================================================== --- llvm/trunk/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test +++ llvm/trunk/test/tools/llvm-readobj/elf-non-dynamic-in-pt-dynamic.test @@ -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 - -# WARNING: warning: '[[FILE]]': The SHT_DYNAMIC section '.dynamic' is not at the start of PT_DYNAMIC segment +# 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]]': 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 @@ - 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 @@ - 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=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" +# 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: ] + +# 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: llvm/trunk/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/ELFDumper.cpp +++ llvm/trunk/tools/llvm-readobj/ELFDumper.cpp @@ -218,6 +218,8 @@ S->sh_entsize, ObjF->getFileName()}); } + std::pair + findDynamic(const ELFFile *Obj); void loadDynamicTable(const ELFFile *Obj); void parseDynamicTable(); @@ -1417,7 +1419,8 @@ } template -void ELFDumper::loadDynamicTable(const ELFFile *Obj) { +std::pair +ELFDumper::findDynamic(const ELFFile *Obj) { // Try to locate the PT_DYNAMIC header. const Elf_Phdr *DynamicPhdr = nullptr; for (const Elf_Phdr &Phdr : @@ -1438,53 +1441,112 @@ 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 +void ELFDumper::loadDynamicTable(const ELFFile *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().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().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(); + // 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. + + 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 (!IsPhdrTableValid && !IsSecTableValid) { + reportWarning(createError("no valid dynamic table was found"), + ObjF->getFileName()); return; } - 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()); + // 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; + } - 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()); + parseDynamicTable(); } template