From ab0f993654f61a1209576733794347b4c2fcdc82 Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Mon, 9 Nov 2020 14:52:46 +0300 Subject: [PATCH] [obj2yaml] - Dump section offsets in some cases. Currently we never dump the `sh_offset` key. Though it sometimes an important information. To reduce the noise this patch implements the following logic: 1) The "Offset" key for the first section is always emitted. 2) If we can derive the offset for a next section naturally, then the "Offset" key is omitted. By "naturally" I mean that section[X] offset is expected to be: ``` offsetOf(section[X]) == alignTo(section[X - 1].sh_offset + section[X - 1].sh_size, section[X].sh_addralign) ``` So, when it has the expected value, we omit it from the output. Differential revision: https://reviews.llvm.org/D91152 --- include/llvm/ObjectYAML/ELFYAML.h | 3 + lib/ObjectYAML/ELFEmitter.cpp | 4 +- test/Object/obj2yaml.test | 4 + test/tools/obj2yaml/ELF/offset.yaml | 260 ++++++++++++++++++++++++++++ tools/obj2yaml/elf2yaml.cpp | 38 ++++ 5 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 test/tools/obj2yaml/ELF/offset.yaml diff --git a/include/llvm/ObjectYAML/ELFYAML.h b/include/llvm/ObjectYAML/ELFYAML.h index 058d78d4f4f..9015fb680b6 100644 --- a/include/llvm/ObjectYAML/ELFYAML.h +++ b/include/llvm/ObjectYAML/ELFYAML.h @@ -658,6 +658,9 @@ struct Object { unsigned getMachine() const; }; +bool shouldAllocateFileSpace(ArrayRef Phdrs, + const NoBitsSection &S); + } // end namespace ELFYAML } // end namespace llvm diff --git a/lib/ObjectYAML/ELFEmitter.cpp b/lib/ObjectYAML/ELFEmitter.cpp index b31b7681ada..b8386bd46be 100644 --- a/lib/ObjectYAML/ELFEmitter.cpp +++ b/lib/ObjectYAML/ELFEmitter.cpp @@ -1135,8 +1135,8 @@ void ELFState::setProgramHeaderLayout(std::vector &PHeaders, } } -static bool shouldAllocateFileSpace(ArrayRef Phdrs, - const ELFYAML::NoBitsSection &S) { +bool llvm::ELFYAML::shouldAllocateFileSpace( + ArrayRef Phdrs, const ELFYAML::NoBitsSection &S) { for (const ELFYAML::ProgramHeader &PH : Phdrs) { auto It = llvm::find_if( PH.Chunks, [&](ELFYAML::Chunk *C) { return C->Name == S.Name; }); diff --git a/test/Object/obj2yaml.test b/test/Object/obj2yaml.test index ea6194dee9a..84044040645 100644 --- a/test/Object/obj2yaml.test +++ b/test/Object/obj2yaml.test @@ -362,6 +362,7 @@ # ELF-MIPSEL-NEXT: Type: SHT_REL # ELF-MIPSEL-NEXT: Link: .symtab # ELF-MIPSEL-NEXT: AddressAlign: 0x4 +# ELF-MIPSEL-NEXT: Offset: 0x434 # ELF-MIPSEL-NEXT: Info: .text # ELF-MIPSEL-NEXT: Relocations: # ELF-MIPSEL-NEXT: - Symbol: _gp_disp @@ -385,6 +386,7 @@ # ELF-MIPSEL-NEXT: Type: SHT_PROGBITS # ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] # ELF-MIPSEL-NEXT: AddressAlign: 0x4 +# ELF-MIPSEL-NEXT: Offset: 0x80 # ELF-MIPSEL-NEXT: - Name: .bss # ELF-MIPSEL-NEXT: Type: SHT_NOBITS # ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] @@ -482,6 +484,7 @@ # ELF-MIPS64EL-NEXT: Type: SHT_RELA # ELF-MIPS64EL-NEXT: Link: .symtab # ELF-MIPS64EL-NEXT: AddressAlign: 0x8 +# ELF-MIPS64EL-NEXT: Offset: 0x410 # ELF-MIPS64EL-NEXT: Info: .data # ELF-MIPS64EL-NEXT: Relocations: # ELF-MIPS64EL-NEXT: - Symbol: zed @@ -490,6 +493,7 @@ # ELF-MIPS64EL-NEXT: Type: SHT_NOBITS # ELF-MIPS64EL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] # ELF-MIPS64EL-NEXT: AddressAlign: 0x10 +# ELF-MIPS64EL-NEXT: Offset: 0x50 # ELF-MIPS64EL-NEXT: - Name: .MIPS.options # ELF-MIPS64EL-NEXT: Type: SHT_MIPS_OPTIONS # ELF-MIPS64EL-NEXT: Flags: [ SHF_ALLOC, SHF_MIPS_NOSTRIP ] diff --git a/test/tools/obj2yaml/ELF/offset.yaml b/test/tools/obj2yaml/ELF/offset.yaml new file mode 100644 index 00000000000..417c92aed1f --- /dev/null +++ b/test/tools/obj2yaml/ELF/offset.yaml @@ -0,0 +1,260 @@ +## Check how the "Offset" field is dumped by obj2yaml. +## For each section we calulate the expected offset. +## When it does not match the actual offset, we emit the "Offset" key. + +# RUN: yaml2obj %s -o %t1.o +# RUN: obj2yaml %t1.o | FileCheck %s --check-prefix=BASIC + +# BASIC: --- !ELF +# BASIC-NEXT: FileHeader: +# BASIC-NEXT: Class: ELFCLASS64 +# BASIC-NEXT: Data: ELFDATA2LSB +# BASIC-NEXT: Type: ET_REL +# BASIC-NEXT: Sections: +# BASIC-NEXT: - Name: .foo1 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: Content: '00' +# BASIC-NEXT: - Name: .foo2 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: Content: '00' +# BASIC-NEXT: - Name: .foo3 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: Content: '00' +# BASIC-NEXT: - Name: .bar1 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: Offset: 0x100 +# BASIC-NEXT: Content: '00' +# BASIC-NEXT: - Name: .bar2 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: AddressAlign: 0x10 +# BASIC-NEXT: Content: '00' +# BASIC-NEXT: - Name: .bar3 +# BASIC-NEXT: Type: SHT_PROGBITS +# BASIC-NEXT: AddressAlign: 0x10 +# BASIC-NEXT: Offset: 0x200 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: +## The offset of .foo1 by default is 0x40, because it is placed right +## after the ELF header. In this case we don't dump the "Offset" key, +## because the file offset is naturally expected. + - Name: .foo1 + Type: SHT_PROGBITS + Size: 1 + Offset: [[FIRSTOFF=]] + AddressAlign: [[FIRSTADDRALIGN=0]] +## Offset of .foo2 == offset of .foo1 + size of .foo1. +## We don't dump the "Offset" key in this case. +## sh_offset of .foo2 is 0x41. + - Name: .foo2 + Type: SHT_PROGBITS + Size: 1 +## Offset of .foo3 == offset of .foo2 + size of .foo2, +## We don't dump the "Offset" key in this case. +## sh_offset of .foo3 is 0x42. + - Name: .foo3 + Type: SHT_PROGBITS + Size: 1 +## Offset of .bar1 != offset of .foo3 + size of .foo3. +## We dump the "Offset" key in this case. +## sh_offset of .bar1 is 0x100. + - Name: .bar1 + Type: SHT_PROGBITS + Offset: 0x100 + Size: 1 +## [Offset of .bar1 + size of .bar1] aligned by 0x10 is equal to the offset +## of .bar2. We don't dump the "Offset" key in this case. +## sh_offset of .bar2 is 0x110. + - Name: .bar2 + Type: SHT_PROGBITS + AddressAlign: 0x10 + Offset: 0x110 + Size: 1 +## [Offset of .bar2 + size of .bar2] aligned by 0x10 is not equal to the offset +## of .bar3. We dump the "Offset" key in this case. +## sh_offset of .bar3 is 0x200. + - Name: .bar3 + Type: SHT_PROGBITS + AddressAlign: 0x10 + Offset: 0x200 + +## Show we dump the "Offset" key for the first section when +## it has an unexpected file offset. + +# RUN: yaml2obj %s -DFIRSTOFF=0x40 -o %t2a.o +# RUN: obj2yaml %t2a.o | FileCheck %s --check-prefix=BASIC +# RUN: yaml2obj %s -DFIRSTOFF=0x41 -o %t2b.o +# RUN: obj2yaml %t2b.o | FileCheck %s --check-prefix=FIRSTSEC + +# FIRSTSEC: Sections: +# FIRSTSEC-NEXT: - Name: .foo1 +# FIRSTSEC-NEXT: Type: SHT_PROGBITS +# FIRSTSEC-NEXT: Offset: 0x41 +# FIRSTSEC-NEXT: Content: '00' + +## Test that we take the alignment of the first section into account +## when calculating the expected offset for it. In this case we don't +## dump the "Offset", because it is expected. + +# RUN: yaml2obj %s -DFIRSTOFF=0x80 -DFIRSTADDRALIGN=0x80 -o %t3.o +# RUN: obj2yaml %t3.o | FileCheck %s --check-prefix=FIRSTSECALIGN + +# FIRSTSECALIGN: - Name: .foo1 +# FIRSTSECALIGN-NEXT: Type: SHT_PROGBITS +# FIRSTSECALIGN-NEXT: AddressAlign: 0x80 +# FIRSTSECALIGN-NEXT: Content: '00' +# FIRSTSECALIGN-NEXT: - Name: + +## Test that we take the program headers offset and size into account when calculating +## the expected file offset of the first section. + +# RUN: yaml2obj %s --docnum=2 -o %t4a.o +# RUN: obj2yaml %t4a.o | FileCheck %s --check-prefix=FIRSTSECPHDRS +## The expected file offset of the first section is: +## 0x40 (start of program headers) + 0x38 (size of program headers) * 2(number of program headers) = 0xB0 +# RUN: yaml2obj %s --docnum=2 -DFIRSTOFF=0xB0 -o %t4b.o +# RUN: obj2yaml %t4b.o | FileCheck %s --check-prefix=FIRSTSECPHDRS +# RUN: yaml2obj %s --docnum=2 -DFIRSTOFF=0xB1 -o %t4c.o +# RUN: obj2yaml %t4c.o | FileCheck %s --check-prefixes=FIRSTSECPHDRS,FIRSTSECPHDRSOFFSET + +# FIRSTSECPHDRS: Sections: +# FIRSTSECPHDRS-NEXT: - Name: .foo +# FIRSTSECPHDRS-NEXT: Type: SHT_PROGBITS +# FIRSTSECPHDRSOFFSET-NEXT: Offset: 0xB1 +# FIRSTSECPHDRS-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: + - Name: .foo + Type: SHT_PROGBITS + Offset: [[FIRSTOFF=]] +ProgramHeaders: + - Type: PT_LOAD + - Type: PT_LOAD + +## Test that when there are no program headers in the file, we don't take SHT_NOBITS +## section sizes into account, but respect their alignment when calculating the expected +## section offsets. + +# RUN: yaml2obj %s --docnum=3 -o %t5.o +# RUN: obj2yaml %t5.o | FileCheck %s --check-prefix=NOBITS + +# NOBITS: Sections: +# NOBITS-NEXT: - Name: .progbits1 +# NOBITS-NEXT: Type: SHT_PROGBITS +# NOBITS-NEXT: Content: '00' +# NOBITS-NEXT: - Name: .nobits1 +# NOBITS-NEXT: Type: SHT_NOBITS +# NOBITS-NEXT: Size: 0x10 +# NOBITS-NEXT: - Name: .progbits2 +# NOBITS-NEXT: Type: SHT_PROGBITS +# NOBITS-NEXT: Content: '0000' +# NOBITS-NEXT: - Name: .nobits2 +# NOBITS-NEXT: Type: SHT_NOBITS +# NOBITS-NEXT: AddressAlign: 0x100 +# NOBITS-NEXT: Size: 0x100 +# NOBITS-NEXT: - Name: .progbits3 +# NOBITS-NEXT: Type: SHT_PROGBITS +# NOBITS-NEXT: Content: '000000' +# NOBITS-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: +## sh_offset == 0x40. + - Name: .progbits1 + Type: SHT_PROGBITS + Size: 0x1 +## sh_offset == 0x41. + - Name: .nobits1 + Type: SHT_NOBITS + Size: 0x10 +## sh_offset == 0x41. + - Name: .progbits2 + Type: SHT_PROGBITS + Size: 0x2 +## sh_offset == 0x100. + - Name: .nobits2 + Type: SHT_NOBITS + Size: 0x100 + AddressAlign: 0x100 +## sh_offset == 0x100. + - Name: .progbits3 + Type: SHT_PROGBITS + Size: 0x3 + +## Check that we might take sizes of SHT_NOBITS sections into account when calculating +## the expected offsets when there are program headers in the file. The rule is the following: +## we assume that the file space is allocated for the SHT_NOBITS section when there are +## other non-nobits sections in the same segment that follows it. + +# RUN: yaml2obj %s --docnum=4 -o %t6.o +# RUN: obj2yaml %t6.o | FileCheck %s --check-prefix=NOBITS-PHDRS + +# NOBITS-PHDRS: Sections: +# NOBITS-PHDRS-NEXT: - Name: .nobits1 +# NOBITS-PHDRS-NEXT: Type: SHT_NOBITS +# NOBITS-PHDRS-NEXT: Size: 0x1 +# NOBITS-PHDRS-NEXT: - Name: .progbits +# NOBITS-PHDRS-NEXT: Type: SHT_PROGBITS +# NOBITS-PHDRS-NEXT: Content: '0000' +# NOBITS-PHDRS-NEXT: - Name: .nobits3 +# NOBITS-PHDRS-NEXT: Type: SHT_NOBITS +# NOBITS-PHDRS-NEXT: Size: 0x100 +# NOBITS-PHDRS-NEXT: - Name: .nobits4 +# NOBITS-PHDRS-NEXT: Type: SHT_NOBITS +# NOBITS-PHDRS-NEXT: Size: 0x200 +# NOBITS-PHDRS-NEXT: - Name: .nobits5 +# NOBITS-PHDRS-NEXT: Type: SHT_NOBITS +# NOBITS-PHDRS-NEXT: Offset: 0x100 +# NOBITS-PHDRS-NEXT: Size: 0x300 +# NOBITS-PHDRS-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: +## sh_offset == 0xe8. + - Name: .nobits1 + Type: SHT_NOBITS + Size: 0x1 +## sh_offset == 0xe9. + - Name: .progbits + Type: SHT_PROGBITS + Size: 0x2 +## sh_offset == 0xeb. + - Name: .nobits3 + Type: SHT_NOBITS + Size: 0x100 +## sh_offset == 0xeb. + - Name: .nobits4 + Type: SHT_NOBITS + Size: 0x200 +## sh_offset == 0x100. + - Name: .nobits5 + Type: SHT_NOBITS + Size: 0x300 + Offset: 0x100 +ProgramHeaders: + - Type: PT_LOAD + FirstSec: .nobits1 + LastSec: .progbits + - Type: PT_LOAD + FirstSec: .nobits3 + LastSec: .nobits4 + - Type: PT_LOAD + FirstSec: .nobits5 + LastSec: .nobits5 diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index 4f5ff19806f..3aa74bab0c1 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -232,6 +232,40 @@ bool ELFDumper::shouldPrintSection(const ELFYAML::Section &S, return true; } +template +static void dumpSectionOffsets(const typename ELFT::Ehdr &Header, + ArrayRef Phdrs, + std::vector> &V, + ArrayRef S) { + uint64_t ExpectedOffset; + if (Header.e_phoff > 0) + ExpectedOffset = Header.e_phoff + Header.e_phentsize * Header.e_phnum; + else + ExpectedOffset = sizeof(typename ELFT::Ehdr); + + for (const std::unique_ptr &C : + makeArrayRef(V).drop_front()) { + ELFYAML::Section &Sec = *cast(C.get()); + const typename ELFT::Shdr &SecHdr = S[Sec.OriginalSecNdx]; + + ExpectedOffset = + alignTo(ExpectedOffset, SecHdr.sh_addralign ? SecHdr.sh_addralign : 1); + + // We only set the "Offset" field when it can't be naturally derived + // from the offset and size of the previous section. This reduces + // the noise in the YAML output. + if (SecHdr.sh_offset != ExpectedOffset) + Sec.Offset = (yaml::Hex64)SecHdr.sh_offset; + + if (Sec.Type == ELF::SHT_NOBITS && + !ELFYAML::shouldAllocateFileSpace(Phdrs, + *cast(&Sec))) + ExpectedOffset = SecHdr.sh_offset; + else + ExpectedOffset = SecHdr.sh_offset + SecHdr.sh_size; + } +} + template Expected ELFDumper::dump() { auto Y = std::make_unique(); @@ -321,6 +355,9 @@ template Expected ELFDumper::dump() { return PhdrsOrErr.takeError(); Y->ProgramHeaders = std::move(*PhdrsOrErr); + dumpSectionOffsets(Obj.getHeader(), Y->ProgramHeaders, Chunks, + Sections); + // Dump DWARF sections. Y->DWARF = dumpDWARFSections(Chunks); @@ -397,6 +434,7 @@ ELFDumper::dumpProgramHeaders( if (!PH.FirstSec) PH.FirstSec = S.Name; PH.LastSec = S.Name; + PH.Chunks.push_back(C.get()); } }