Index: llvm/trunk/test/Object/corrupt.test =================================================================== --- llvm/trunk/test/Object/corrupt.test +++ llvm/trunk/test/Object/corrupt.test @@ -62,11 +62,11 @@ 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 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 @@ -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: llvm/trunk/test/tools/llvm-readobj/elf-malformed-pt-dynamic.test =================================================================== --- llvm/trunk/test/tools/llvm-readobj/elf-malformed-pt-dynamic.test +++ llvm/trunk/test/tools/llvm-readobj/elf-malformed-pt-dynamic.test @@ -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: 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 @@ -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: llvm/trunk/tools/llvm-readobj/ELFDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/ELFDumper.cpp +++ llvm/trunk/tools/llvm-readobj/ELFDumper.cpp @@ -203,7 +203,8 @@ {ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize}); } - void parseDynamicTable(ArrayRef LoadSegments); + void loadDynamicTable(const ELFFile *Obj); + void parseDynamicTable(); void printValue(uint64_t Type, uint64_t Value); @@ -1329,21 +1330,72 @@ } template -ELFDumper::ELFDumper(const object::ELFObjectFile *ObjF, - ScopedPrinter &Writer) - : ObjDumper(Writer), ObjF(ObjF) { - SmallVector LoadSegments; - const ELFFile *Obj = ObjF->getELFFile(); +void ELFDumper::loadDynamicTable(const ELFFile *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 +ELFDumper::ELFDumper(const object::ELFObjectFile *ObjF, + ScopedPrinter &Writer) + : ObjDumper(Writer), ObjF(ObjF) { + const ELFFile *Obj = ObjF->getELFFile(); + for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) { switch (Sec.sh_type) { case ELF::SHT_SYMTAB: @@ -1390,7 +1442,7 @@ } } - parseDynamicTable(LoadSegments); + loadDynamicTable(Obj); if (opts::Output == opts::GNU) ELFDumperStyle.reset(new GNUStyle(Writer, this)); @@ -1398,9 +1450,7 @@ ELFDumperStyle.reset(new LLVMStyle(Writer, this)); } -template -void ELFDumper::parseDynamicTable( - ArrayRef LoadSegments) { +template void ELFDumper::parseDynamicTable() { auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * { auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr); if (!MappedAddrOrError) Index: llvm/trunk/tools/llvm-readobj/llvm-readobj.h =================================================================== --- llvm/trunk/tools/llvm-readobj/llvm-readobj.h +++ llvm/trunk/tools/llvm-readobj/llvm-readobj.h @@ -22,6 +22,7 @@ // 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 T error(llvm::Expected &&E) { Index: llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/trunk/tools/llvm-readobj/llvm-readobj.cpp @@ -373,6 +373,11 @@ exit(1); } +void reportWarning(Twine Msg) { + errs() << "\n"; + WithColor::warning(errs()) << Msg << "\n"; +} + void error(Error EC) { if (!EC) return;