[yaml2obj] - Automatically assign sh_addr for allocatable sections.

I've noticed that it is not convenient to create YAMLs from
binaries (using obj2yaml) that have to be test cases for obj2yaml
later (after applying yaml2obj).

The problem, for example is that obj2yaml emits "DynamicSymbols:"
key instead of .dynsym. It also does not create .dynstr.
And when a YAML document without explicitly defined .dynsym/.dynstr
is given to yaml2obj, we have issues:

1) These sections are placed after non-allocatable sections (I've fixed it in D74756).
2) They have VA == 0. User needs create descriptions for such sections explicitly manually
    to set a VA.

This patch addresses (2). I suggest to let yaml2obj assign virtual addresses by itself.
It makes an output binary to be much closer to "normal" ELF.
(It is still possible to use "Address: 0x0" for a section to get the original behavior
if it is needed)

Differential revision: https://reviews.llvm.org/D74764
This commit is contained in:
Georgii Rymar 2020-02-18 16:49:12 +03:00
parent 635034f193
commit 31f2ad9c36
9 changed files with 146 additions and 16 deletions

View File

@ -167,7 +167,7 @@ struct Chunk {
struct Section : public Chunk {
ELF_SHT Type;
Optional<ELF_SHF> Flags;
llvm::yaml::Hex64 Address;
Optional<llvm::yaml::Hex64> Address;
StringRef Link;
llvm::yaml::Hex64 AddressAlign;
Optional<llvm::yaml::Hex64> EntSize;

View File

@ -128,6 +128,7 @@ template <class ELFT> class ELFState {
NameToIdxMap DynSymN2I;
ELFYAML::Object &Doc;
uint64_t LocationCounter = 0;
bool HasError = false;
yaml::ErrorHandler ErrHandler;
void reportError(const Twine &Msg);
@ -218,6 +219,8 @@ template <class ELFT> class ELFState {
ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH);
void assignSectionAddress(Elf_Shdr &SHeader, ELFYAML::Section *YAMLSec);
public:
static bool writeELF(raw_ostream &OS, ELFYAML::Object &Doc,
yaml::ErrorHandler EH);
@ -390,6 +393,8 @@ bool ELFState<ELFT>::initImplicitHeader(ContiguousBlobAccumulator &CBA,
else
return false;
LocationCounter += Header.sh_size;
// Override section fields if requested.
overrideFields<ELFT>(YAMLSec, Header);
return true;
@ -413,6 +418,7 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
for (const std::unique_ptr<ELFYAML::Chunk> &D : Doc.Chunks) {
if (auto S = dyn_cast<ELFYAML::Fill>(D.get())) {
writeFill(*S, CBA);
LocationCounter += S->Size;
continue;
}
@ -438,9 +444,10 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
SHeader.sh_type = Sec->Type;
if (Sec->Flags)
SHeader.sh_flags = *Sec->Flags;
SHeader.sh_addr = Sec->Address;
SHeader.sh_addralign = Sec->AddressAlign;
assignSectionAddress(SHeader, Sec);
if (!Sec->Link.empty())
SHeader.sh_link = toSectionIndex(Sec->Link, Sec->Name);
@ -500,11 +507,34 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
llvm_unreachable("Unknown section type");
}
LocationCounter += SHeader.sh_size;
// Override section fields if requested.
overrideFields<ELFT>(Sec, SHeader);
}
}
template <class ELFT>
void ELFState<ELFT>::assignSectionAddress(Elf_Shdr &SHeader,
ELFYAML::Section *YAMLSec) {
if (YAMLSec && YAMLSec->Address) {
SHeader.sh_addr = *YAMLSec->Address;
LocationCounter = *YAMLSec->Address;
return;
}
// sh_addr represents the address in the memory image of a process. Sections
// in a relocatable object file or non-allocatable sections do not need
// sh_addr assignment.
if (Doc.Header.Type.value == ELF::ET_REL ||
!(SHeader.sh_flags & ELF::SHF_ALLOC))
return;
LocationCounter =
alignTo(LocationCounter, SHeader.sh_addralign ? SHeader.sh_addralign : 1);
SHeader.sh_addr = LocationCounter;
}
static size_t findFirstNonGlobal(ArrayRef<ELFYAML::Symbol> Symbols) {
for (size_t I = 0; I < Symbols.size(); ++I)
if (Symbols[I].Binding.value != ELF::STB_LOCAL)
@ -629,7 +659,8 @@ void ELFState<ELFT>::initSymtabSectionHeader(Elf_Shdr &SHeader,
? (uint64_t)(*YAMLSec->EntSize)
: sizeof(Elf_Sym);
SHeader.sh_addralign = YAMLSec ? (uint64_t)YAMLSec->AddressAlign : 8;
SHeader.sh_addr = YAMLSec ? (uint64_t)YAMLSec->Address : 0;
assignSectionAddress(SHeader, YAMLSec);
auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
if (RawSec && (RawSec->Content || RawSec->Size)) {
@ -678,8 +709,7 @@ void ELFState<ELFT>::initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name,
// If the section is explicitly described in the YAML
// then we want to use its section address.
if (YAMLSec)
SHeader.sh_addr = YAMLSec->Address;
assignSectionAddress(SHeader, YAMLSec);
}
template <class ELFT> void ELFState<ELFT>::reportError(const Twine &Msg) {

View File

@ -1013,7 +1013,7 @@ static void commonSectionMapping(IO &IO, ELFYAML::Section &Section) {
IO.mapOptional("Name", Section.Name, StringRef());
IO.mapRequired("Type", Section.Type);
IO.mapOptional("Flags", Section.Flags);
IO.mapOptional("Address", Section.Address, Hex64(0));
IO.mapOptional("Address", Section.Address);
IO.mapOptional("Link", Section.Link, StringRef());
IO.mapOptional("AddressAlign", Section.AddressAlign, Hex64(0));
IO.mapOptional("EntSize", Section.EntSize);

View File

@ -193,9 +193,9 @@ ProgramHeaders:
# CHECK3: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
# CHECK3: [ 1] .dynsym NOBITS 0000000000000000 000040 000018 18 A 2 1 1024
# CHECK3-NEXT: [ 2] .dynstr NOBITS 0000000000000000 000040 000001 00 A 0 0 0
# CHECK3-NEXT: [ 3] .symtab NOBITS 0000000000000000 000040 000018 18 A 4 1 0
# CHECK3-NEXT: [ 4] .strtab NOBITS 0000000000000000 000040 000001 00 A 0 0 0
# CHECK3-NEXT: [ 2] .dynstr NOBITS 0000000000000018 000040 000001 00 A 0 0 0
# CHECK3-NEXT: [ 3] .symtab NOBITS 0000000000000019 000040 000018 18 A 4 1 0
# CHECK3-NEXT: [ 4] .strtab NOBITS 0000000000000031 000040 000001 00 A 0 0 0
# CHECK3-NEXT: [ 5] .shstrtab STRTAB 0000000000000000 000040 00002b 00 0 0 1
--- !ELF

View File

@ -13,7 +13,7 @@
# GNU-VERNEED-NAME-NEXT: 000: 0 (*local*) 2 ()
# GNU-VERNEED-NAME: Version needs section '.gnu.version_r' contains 1 entries:
# GNU-VERNEED-NAME-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 4 (.dynstr)
# GNU-VERNEED-NAME-NEXT: Addr: 0000000000200214 Offset: 0x000044 Link: 4 (.dynstr)
# GNU-VERNEED-NAME-NEXT: 0x0000: Version: 1 File: somefile Cnt: 1
# GNU-VERNEED-NAME-NEXT: 0x0010: Name: Flags: none Version: 2
@ -89,7 +89,7 @@ DynamicSymbols:
# GNU-NOLINK-NEXT: warning: '[[FILE]]': invalid string table linked to SHT_GNU_verneed section with index 2: invalid sh_type for string table section [index 0]: expected SHT_STRTAB, but got SHT_NULL
# GNU-NOLINK-NEXT: 000: 0 (*local*) 2 (<corrupt>)
# GNU-NOLINK: Version needs section '.gnu.version_r' contains 1 entries:
# GNU-NOLINK-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 0 ()
# GNU-NOLINK-NEXT: Addr: 0000000000000004 Offset: 0x000044 Link: 0 ()
# GNU-NOLINK-NEXT: 0x0000: Version: 1 File: <corrupt vn_file: 9> Cnt: 1
# GNU-NOLINK-NEXT: 0x0010: Name: <corrupt> Flags: none Version: 2
@ -231,7 +231,7 @@ DynamicSymbols: []
# LLVM-OFFSET-EQ-NEXT: ]
# GNU-OFFSET-EQ: Version needs section '.gnu.version_r' contains 1 entries:
# GNU-OFFSET-EQ-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 1 (.mystrtab)
# GNU-OFFSET-EQ-NEXT: Addr: 0000000000000004 Offset: 0x000044 Link: 1 (.mystrtab)
# GNU-OFFSET-EQ-NEXT: 0x0000: Version: 1 File: <corrupt vn_file: 1> Cnt: 1
# GNU-OFFSET-EQ-NEXT: 0x0010: Name: <corrupt> Flags: none Version: 0
@ -562,7 +562,7 @@ DynamicSymbols:
# GNU-CUSTOM-DYNSTR-NEXT: Addr: 0000000000000000 Offset: 0x000040 Link: 4 (.dynsym)
# GNU-CUSTOM-DYNSTR-NEXT: 000: 0 (*local*) 2 (bcdefghij)
# GNU-CUSTOM-DYNSTR: Version needs section '.gnu.version_r' contains 1 entries:
# GNU-CUSTOM-DYNSTR-NEXT: Addr: 0000000000000000 Offset: 0x000044 Link: 3 (.custom.dynstr)
# GNU-CUSTOM-DYNSTR-NEXT: Addr: 0000000000000004 Offset: 0x000044 Link: 3 (.custom.dynstr)
# GNU-CUSTOM-DYNSTR-NEXT: 0x0000: Version: 1 File: j Cnt: 1
# GNU-CUSTOM-DYNSTR-NEXT: 0x0010: Name: bcdefghij Flags: none Version: 2

View File

@ -275,7 +275,7 @@ DynamicSymbols:
# GNU-NEXT: 004: 5 (v2) 6 (v3)
# GNU-EMPTY:
# GNU-NEXT: Version definition section '.gnu.version_d' contains 6 entries:
# GNU-NEXT: Addr: 0000000000000000 Offset: 0x00004c Link: 5 (.dynstr)
# GNU-NEXT: Addr: 000000000000000c Offset: 0x00004c Link: 5 (.dynstr)
# GNU-NEXT: 0x0000: Rev: 1 Flags: none Index: 0 Cnt: 1 Name: VERSION1
# GNU-NEXT: 0x001c: Rev: 1 Flags: BASE Index: 0 Cnt: 1 Name: VERSION1
# GNU-NEXT: 0x0038: Rev: 1 Flags: WEAK Index: 0 Cnt: 1 Name: VERSION1
@ -286,7 +286,7 @@ DynamicSymbols:
# GNU-NEXT: 0x00b0: Parent 2: VERSION3
# GNU-EMPTY:
# GNU-NEXT: Version needs section '.gnu.version_r' contains 2 entries:
# GNU-NEXT: Addr: 0000000000000000 Offset: 0x000104 Link: 5 (.dynstr)
# GNU-NEXT: Addr: 00000000000000c4 Offset: 0x000104 Link: 5 (.dynstr)
# GNU-NEXT: 0x0000: Version: 1 File: verneed1.so.0 Cnt: 5
# GNU-NEXT: 0x0010: Name: v1 Flags: BASE Version: 0
# GNU-NEXT: 0x0020: Name: v1 Flags: WEAK Version: 0

View File

@ -45,6 +45,7 @@ Sections:
# INVALID-NEXT: - Name: .gnu.hash.empty
# INVALID-NEXT: Type: SHT_GNU_HASH
# INVALID-NEXT: Flags: [ SHF_ALLOC ]
# INVALID-NEXT: Address: 0x000000000000000F
# INVALID-NEXT: Header:
# INVALID-NEXT: SymNdx: 0x00000000
# INVALID-NEXT: Shift2: 0x00000000

View File

@ -0,0 +1,98 @@
## Test that yaml2obj automatically assigns sh_addr to allocatable sections for ET_EXEC/ET_DYN files.
# RUN: yaml2obj %s -o %t.so -D TYPE=ET_DYN
# RUN: llvm-readelf --sections %t.so | FileCheck %s --check-prefix=EXE-DSO
# RUN: yaml2obj %s -o %t -D TYPE=ET_EXEC
# RUN: llvm-readelf --sections %t | FileCheck %s --check-prefix=EXE-DSO
# RUN: yaml2obj %s -o %t.o -D TYPE=ET_REL
# RUN: llvm-readelf --sections %t.o | FileCheck %s --check-prefix=REL
## We assign virtual addresses to allocatable sections automatically for executables and shared libraries.
# EXE-DSO: Section Headers:
# EXE-DSO-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
# EXE-DSO-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
# EXE-DSO-NEXT: [ 1] .text.any.addr PROGBITS 0000000000001000 000040 000003 00 A 0 0 0
# EXE-DSO-NEXT: [ 2] .text.shsize PROGBITS 0000000000001003 000043 001234 00 A 0 0 0
# EXE-DSO-NEXT: [ 3] .text.align PROGBITS 0000000000001100 000100 000004 00 A 0 0 256
# EXE-DSO-NEXT: [ 4] .data.any.addr PROGBITS 0000000000002000 000104 000001 00 A 0 0 0
# EXE-DSO-NEXT: [ 5] .data.after.fill PROGBITS 0000000000002101 000205 000001 00 A 0 0 0
# EXE-DSO-NEXT: [ 6] .data.return.back PROGBITS 0000000000001500 000206 000001 00 A 0 0 0
# EXE-DSO-NEXT: [ 7] .data.return.back.foo PROGBITS 0000000000001501 000207 000000 00 A 0 0 0
# EXE-DSO-NEXT: [ 8] .dynsym DYNSYM 0000000000001508 000208 000018 18 A 9 1 8
# EXE-DSO-NEXT: [ 9] .dynstr STRTAB 0000000000001520 000220 000001 00 A 0 0 1
# EXE-DSO-NEXT: [10] .strtab STRTAB 0000000000000000 000221 000001 00 0 0 1
# EXE-DSO-NEXT: [11] .shstrtab STRTAB 0000000000000000 000222 000093 00 0 0 1
## We do not assign virtual addresses to allocatable sections in a relocatable object
## unless YAML document has an explicit request.
# REL: Section Headers:
# REL-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
# REL-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0
# REL-NEXT: [ 1] .text.any.addr PROGBITS 0000000000001000 000040 000003 00 A 0 0 0
# REL-NEXT: [ 2] .text.shsize PROGBITS 0000000000000000 000043 001234 00 A 0 0 0
# REL-NEXT: [ 3] .text.align PROGBITS 0000000000000000 000100 000004 00 A 0 0 256
# REL-NEXT: [ 4] .data.any.addr PROGBITS 0000000000002000 000104 000001 00 A 0 0 0
# REL-NEXT: [ 5] .data.after.fill PROGBITS 0000000000000000 000205 000001 00 A 0 0 0
# REL-NEXT: [ 6] .data.return.back PROGBITS 0000000000001500 000206 000001 00 A 0 0 0
# REL-NEXT: [ 7] .data.return.back.foo PROGBITS 0000000000000000 000207 000000 00 A 0 0 0
# REL-NEXT: [ 8] .dynsym DYNSYM 0000000000000000 000208 000018 18 A 9 1 8
# REL-NEXT: [ 9] .dynstr STRTAB 0000000000000000 000220 000001 00 A 0 0 1
# REL-NEXT: [10] .strtab STRTAB 0000000000000000 000221 000001 00 0 0 1
# REL-NEXT: [11] .shstrtab STRTAB 0000000000000000 000222 000093 00 0 0 1
--- !ELF
FileHeader:
Class: ELFCLASS64
Data: ELFDATA2LSB
Type: [[TYPE]]
Machine: EM_X86_64
Sections:
## Show we can place a section at any address.
- Name: .text.any.addr
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x1000
Size: 0x3
## Test that ShSize does not affect virtual addresses.
- Name: .text.shsize
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
ShSize: 0x1234
## Show we respect an address align when automatically
## assign virtual addresses.
- Name: .text.align
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
AddressAlign: 0x100
Size: 0x4
## We can set another address for a subsequent section.
- Name: .data.any.addr
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Address: 0x2000
Size: 0x1
## Show that Fill occupies VA space.
- Type: Fill
Pattern: "AABB"
Size: 0x100
- Name: .data.after.fill
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Size: 0x1
## Show we can return back in the address space and
## continue placing sections. The order of sections in the
## section header table will match the order in the YAML description.
- Name: .data.return.back
Address: 0x1500
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
Size: 0x1
- Name: .data.return.back.foo
Type: SHT_PROGBITS
Flags: [ SHF_ALLOC ]
## Used to trigger creation of .dynsym and .dynstr.
DynamicSymbols: []

View File

@ -502,7 +502,8 @@ Error ELFDumper<ELFT>::dumpCommonSection(const Elf_Shdr *Shdr,
S.Type = Shdr->sh_type;
if (Shdr->sh_flags)
S.Flags = static_cast<ELFYAML::ELF_SHF>(Shdr->sh_flags);
S.Address = Shdr->sh_addr;
if (Shdr->sh_addr)
S.Address = static_cast<uint64_t>(Shdr->sh_addr);
S.AddressAlign = Shdr->sh_addralign;
if (Shdr->sh_entsize)
S.EntSize = static_cast<llvm::yaml::Hex64>(Shdr->sh_entsize);