--- /dev/null
+## 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
{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);
}
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:
}
}
- parseDynamicTable(LoadSegments);
+ loadDynamicTable(Obj);
if (opts::Output == opts::GNU)
ELFDumperStyle.reset(new GNUStyle<ELFT>(Writer, this));
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)