From 31f2ad9c368d47721508cbd0d120d626f9041715 Mon Sep 17 00:00:00 2001 From: Georgii Rymar Date: Tue, 18 Feb 2020 16:49:12 +0300 Subject: [PATCH] [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 --- llvm/include/llvm/ObjectYAML/ELFYAML.h | 2 +- llvm/lib/ObjectYAML/ELFEmitter.cpp | 38 ++++++- llvm/lib/ObjectYAML/ELFYAML.cpp | 2 +- .../llvm-objcopy/ELF/only-keep-debug.test | 6 +- .../llvm-readobj/ELF/verneed-invalid.test | 8 +- .../tools/llvm-readobj/ELF/versioninfo.test | 4 +- .../tools/obj2yaml/elf-gnu-hash-section.yaml | 1 + .../yaml2obj/ELF/section-address-assign.yaml | 98 +++++++++++++++++++ llvm/tools/obj2yaml/elf2yaml.cpp | 3 +- 9 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 llvm/test/tools/yaml2obj/ELF/section-address-assign.yaml diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index 0d808b660e15..60bb2375901b 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -167,7 +167,7 @@ struct Chunk { struct Section : public Chunk { ELF_SHT Type; Optional Flags; - llvm::yaml::Hex64 Address; + Optional Address; StringRef Link; llvm::yaml::Hex64 AddressAlign; Optional EntSize; diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index a07b83a6efcf..78684883da1a 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -128,6 +128,7 @@ template 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 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::initImplicitHeader(ContiguousBlobAccumulator &CBA, else return false; + LocationCounter += Header.sh_size; + // Override section fields if requested. overrideFields(YAMLSec, Header); return true; @@ -413,6 +418,7 @@ void ELFState::initSectionHeaders(std::vector &SHeaders, for (const std::unique_ptr &D : Doc.Chunks) { if (auto S = dyn_cast(D.get())) { writeFill(*S, CBA); + LocationCounter += S->Size; continue; } @@ -438,9 +444,10 @@ void ELFState::initSectionHeaders(std::vector &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::initSectionHeaders(std::vector &SHeaders, llvm_unreachable("Unknown section type"); } + LocationCounter += SHeader.sh_size; + // Override section fields if requested. overrideFields(Sec, SHeader); } } +template +void ELFState::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 Symbols) { for (size_t I = 0; I < Symbols.size(); ++I) if (Symbols[I].Binding.value != ELF::STB_LOCAL) @@ -629,7 +659,8 @@ void ELFState::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::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 void ELFState::reportError(const Twine &Msg) { diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp index 6a553a7a9854..58f87b2708e3 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -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); diff --git a/llvm/test/tools/llvm-objcopy/ELF/only-keep-debug.test b/llvm/test/tools/llvm-objcopy/ELF/only-keep-debug.test index 1dc29c973658..8f21bce57823 100644 --- a/llvm/test/tools/llvm-objcopy/ELF/only-keep-debug.test +++ b/llvm/test/tools/llvm-objcopy/ELF/only-keep-debug.test @@ -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 diff --git a/llvm/test/tools/llvm-readobj/ELF/verneed-invalid.test b/llvm/test/tools/llvm-readobj/ELF/verneed-invalid.test index 9c6148338d3f..2988adcce8da 100644 --- a/llvm/test/tools/llvm-readobj/ELF/verneed-invalid.test +++ b/llvm/test/tools/llvm-readobj/ELF/verneed-invalid.test @@ -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 () # 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: Cnt: 1 # GNU-NOLINK-NEXT: 0x0010: Name: 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: Cnt: 1 # GNU-OFFSET-EQ-NEXT: 0x0010: Name: 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 diff --git a/llvm/test/tools/llvm-readobj/ELF/versioninfo.test b/llvm/test/tools/llvm-readobj/ELF/versioninfo.test index 34ed7cd42d5d..da9e3eef087d 100644 --- a/llvm/test/tools/llvm-readobj/ELF/versioninfo.test +++ b/llvm/test/tools/llvm-readobj/ELF/versioninfo.test @@ -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 diff --git a/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml b/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml index 24f087b68812..85492c9f1aad 100644 --- a/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml +++ b/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml @@ -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 diff --git a/llvm/test/tools/yaml2obj/ELF/section-address-assign.yaml b/llvm/test/tools/yaml2obj/ELF/section-address-assign.yaml new file mode 100644 index 000000000000..e163ee80f295 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/section-address-assign.yaml @@ -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: [] diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index cb06e093a2bf..08c3587a821d 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -502,7 +502,8 @@ Error ELFDumper::dumpCommonSection(const Elf_Shdr *Shdr, S.Type = Shdr->sh_type; if (Shdr->sh_flags) S.Flags = static_cast(Shdr->sh_flags); - S.Address = Shdr->sh_addr; + if (Shdr->sh_addr) + S.Address = static_cast(Shdr->sh_addr); S.AddressAlign = Shdr->sh_addralign; if (Shdr->sh_entsize) S.EntSize = static_cast(Shdr->sh_entsize);