Index: test/tools/llvm-readobj/elf-dynamic-not-in-pt-dynamic.test =================================================================== --- /dev/null +++ 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 which is not in PT_DYNAMIC segment. + +# RUN: yaml2obj %s -o %t.o +# RUN: llvm-readobj --dynamic-table %t.o | FileCheck %s +# RUN: llvm-readelf --dynamic-table %t.o | FileCheck %s + +# CHECK: warning: The SHT_DYNAMIC section 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: test/tools/llvm-readobj/elf-malformed-pt-dynamic.test =================================================================== --- test/tools/llvm-readobj/elf-malformed-pt-dynamic.test +++ 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: test/tools/llvm-readobj/elf-not-dynamic-in-pt-dynamic.test =================================================================== --- /dev/null +++ test/tools/llvm-readobj/elf-not-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 and we check that +## show a warning about that. + +# RUN: yaml2obj --docnum=1 %s -o %t.o +# RUN: llvm-readobj --dynamic-table %t.o | FileCheck %s --check-prefixes=WARNING,CHECK +# RUN: llvm-readelf --dynamic-table %t.o | FileCheck %s --check-prefixes=WARNING,CHECK + +# WARNING: warning: The SHT_DYNAMIC section 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: tools/llvm-readobj/ELFDumper.cpp =================================================================== --- tools/llvm-readobj/ELFDumper.cpp +++ 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 findDynamicTable(const ELFFile *Obj); + void parseDynamicTable(); void printValue(uint64_t Type, uint64_t Value); @@ -1317,21 +1318,64 @@ } template -ELFDumper::ELFDumper(const object::ELFObjectFile *ObjF, - ScopedPrinter &Writer) - : ObjDumper(Writer), ObjF(ObjF) { - SmallVector LoadSegments; - const ELFFile *Obj = ObjF->getELFFile(); +void ELFDumper::findDynamicTable(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. + 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 a priority under 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)); + return; + } + + if (DynamicSec->sh_addr + DynamicSec->sh_size > + DynamicPhdr->p_vaddr + DynamicPhdr->p_memsz || + DynamicSec->sh_addr < DynamicPhdr->p_vaddr) + reportWarning("The SHT_DYNAMIC section is not contained within the " + "PT_DYNAMIC segment"); + + if (DynamicSec->sh_addr != DynamicPhdr->p_vaddr) + reportWarning("The SHT_DYNAMIC section is not at the start of " + "PT_DYNAMIC segment"); +} + +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: @@ -1378,7 +1422,8 @@ } } - parseDynamicTable(LoadSegments); + findDynamicTable(Obj); + parseDynamicTable(); if (opts::Output == opts::GNU) ELFDumperStyle.reset(new GNUStyle(Writer, this)); @@ -1386,9 +1431,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: tools/llvm-readobj/llvm-readobj.h =================================================================== --- tools/llvm-readobj/llvm-readobj.h +++ 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: tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- tools/llvm-readobj/llvm-readobj.cpp +++ tools/llvm-readobj/llvm-readobj.cpp @@ -373,6 +373,11 @@ exit(1); } +void reportWarning(Twine Msg) { + outs() << "\n"; + WithColor::warning(outs()) << Msg << "\n"; +} + void error(Error EC) { if (!EC) return;