[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
This commit is contained in:
Georgii Rymar 2020-11-09 14:52:46 +03:00
parent d8f22c7769
commit 5edb90c927
5 changed files with 307 additions and 2 deletions

View File

@ -658,6 +658,9 @@ struct Object {
unsigned getMachine() const; unsigned getMachine() const;
}; };
bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> Phdrs,
const NoBitsSection &S);
} // end namespace ELFYAML } // end namespace ELFYAML
} // end namespace llvm } // end namespace llvm

View File

@ -1135,8 +1135,8 @@ void ELFState<ELFT>::setProgramHeaderLayout(std::vector<Elf_Phdr> &PHeaders,
} }
} }
static bool shouldAllocateFileSpace(ArrayRef<ELFYAML::ProgramHeader> Phdrs, bool llvm::ELFYAML::shouldAllocateFileSpace(
const ELFYAML::NoBitsSection &S) { ArrayRef<ELFYAML::ProgramHeader> Phdrs, const ELFYAML::NoBitsSection &S) {
for (const ELFYAML::ProgramHeader &PH : Phdrs) { for (const ELFYAML::ProgramHeader &PH : Phdrs) {
auto It = llvm::find_if( auto It = llvm::find_if(
PH.Chunks, [&](ELFYAML::Chunk *C) { return C->Name == S.Name; }); PH.Chunks, [&](ELFYAML::Chunk *C) { return C->Name == S.Name; });

View File

@ -362,6 +362,7 @@
# ELF-MIPSEL-NEXT: Type: SHT_REL # ELF-MIPSEL-NEXT: Type: SHT_REL
# ELF-MIPSEL-NEXT: Link: .symtab # ELF-MIPSEL-NEXT: Link: .symtab
# ELF-MIPSEL-NEXT: AddressAlign: 0x4 # ELF-MIPSEL-NEXT: AddressAlign: 0x4
# ELF-MIPSEL-NEXT: Offset: 0x434
# ELF-MIPSEL-NEXT: Info: .text # ELF-MIPSEL-NEXT: Info: .text
# ELF-MIPSEL-NEXT: Relocations: # ELF-MIPSEL-NEXT: Relocations:
# ELF-MIPSEL-NEXT: - Symbol: _gp_disp # ELF-MIPSEL-NEXT: - Symbol: _gp_disp
@ -385,6 +386,7 @@
# ELF-MIPSEL-NEXT: Type: SHT_PROGBITS # ELF-MIPSEL-NEXT: Type: SHT_PROGBITS
# ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] # ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ]
# ELF-MIPSEL-NEXT: AddressAlign: 0x4 # ELF-MIPSEL-NEXT: AddressAlign: 0x4
# ELF-MIPSEL-NEXT: Offset: 0x80
# ELF-MIPSEL-NEXT: - Name: .bss # ELF-MIPSEL-NEXT: - Name: .bss
# ELF-MIPSEL-NEXT: Type: SHT_NOBITS # ELF-MIPSEL-NEXT: Type: SHT_NOBITS
# ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] # ELF-MIPSEL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ]
@ -482,6 +484,7 @@
# ELF-MIPS64EL-NEXT: Type: SHT_RELA # ELF-MIPS64EL-NEXT: Type: SHT_RELA
# ELF-MIPS64EL-NEXT: Link: .symtab # ELF-MIPS64EL-NEXT: Link: .symtab
# ELF-MIPS64EL-NEXT: AddressAlign: 0x8 # ELF-MIPS64EL-NEXT: AddressAlign: 0x8
# ELF-MIPS64EL-NEXT: Offset: 0x410
# ELF-MIPS64EL-NEXT: Info: .data # ELF-MIPS64EL-NEXT: Info: .data
# ELF-MIPS64EL-NEXT: Relocations: # ELF-MIPS64EL-NEXT: Relocations:
# ELF-MIPS64EL-NEXT: - Symbol: zed # ELF-MIPS64EL-NEXT: - Symbol: zed
@ -490,6 +493,7 @@
# ELF-MIPS64EL-NEXT: Type: SHT_NOBITS # ELF-MIPS64EL-NEXT: Type: SHT_NOBITS
# ELF-MIPS64EL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ] # ELF-MIPS64EL-NEXT: Flags: [ SHF_WRITE, SHF_ALLOC ]
# ELF-MIPS64EL-NEXT: AddressAlign: 0x10 # ELF-MIPS64EL-NEXT: AddressAlign: 0x10
# ELF-MIPS64EL-NEXT: Offset: 0x50
# ELF-MIPS64EL-NEXT: - Name: .MIPS.options # ELF-MIPS64EL-NEXT: - Name: .MIPS.options
# ELF-MIPS64EL-NEXT: Type: SHT_MIPS_OPTIONS # ELF-MIPS64EL-NEXT: Type: SHT_MIPS_OPTIONS
# ELF-MIPS64EL-NEXT: Flags: [ SHF_ALLOC, SHF_MIPS_NOSTRIP ] # ELF-MIPS64EL-NEXT: Flags: [ SHF_ALLOC, SHF_MIPS_NOSTRIP ]

View File

@ -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=<none>]]
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=<none>]]
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

View File

@ -232,6 +232,40 @@ bool ELFDumper<ELFT>::shouldPrintSection(const ELFYAML::Section &S,
return true; return true;
} }
template <class ELFT>
static void dumpSectionOffsets(const typename ELFT::Ehdr &Header,
ArrayRef<ELFYAML::ProgramHeader> Phdrs,
std::vector<std::unique_ptr<ELFYAML::Chunk>> &V,
ArrayRef<typename ELFT::Shdr> 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<ELFYAML::Chunk> &C :
makeArrayRef(V).drop_front()) {
ELFYAML::Section &Sec = *cast<ELFYAML::Section>(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<ELFYAML::NoBitsSection>(&Sec)))
ExpectedOffset = SecHdr.sh_offset;
else
ExpectedOffset = SecHdr.sh_offset + SecHdr.sh_size;
}
}
template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() { template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
auto Y = std::make_unique<ELFYAML::Object>(); auto Y = std::make_unique<ELFYAML::Object>();
@ -321,6 +355,9 @@ template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
return PhdrsOrErr.takeError(); return PhdrsOrErr.takeError();
Y->ProgramHeaders = std::move(*PhdrsOrErr); Y->ProgramHeaders = std::move(*PhdrsOrErr);
dumpSectionOffsets<ELFT>(Obj.getHeader(), Y->ProgramHeaders, Chunks,
Sections);
// Dump DWARF sections. // Dump DWARF sections.
Y->DWARF = dumpDWARFSections(Chunks); Y->DWARF = dumpDWARFSections(Chunks);
@ -397,6 +434,7 @@ ELFDumper<ELFT>::dumpProgramHeaders(
if (!PH.FirstSec) if (!PH.FirstSec)
PH.FirstSec = S.Name; PH.FirstSec = S.Name;
PH.LastSec = S.Name; PH.LastSec = S.Name;
PH.Chunks.push_back(C.get());
} }
} }