Index: llvm/include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- llvm/include/llvm/ObjectYAML/ELFYAML.h +++ llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -98,6 +98,10 @@ Optional> Sections; Optional> Excluded; Optional NoHeaders; + + static constexpr StringRef BeforeSec = "BeforeSecData"; + static constexpr StringRef AfterSec = "AfterSecData"; + Optional Location; }; struct Symbol { Index: llvm/lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFEmitter.cpp +++ llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -109,6 +109,13 @@ OS.write(Ptr, Size); } + void writeAt(uint64_t Pos, const char *Ptr, uint64_t Size) { + if (!checkLimit(0)) + return; + assert(Pos - InitialOffset + Size <= Buf.size()); + memcpy(Buf.data() + Pos - InitialOffset, Ptr, Size); + } + void write(unsigned char C) { if (checkLimit(1)) OS.write(C); @@ -212,8 +219,8 @@ void initProgramHeaders(std::vector &PHeaders); bool initImplicitHeader(ContiguousBlobAccumulator &CBA, Elf_Shdr &Header, StringRef SecName, ELFYAML::Section *YAMLSec); - void initSectionHeaders(std::vector &SHeaders, - ContiguousBlobAccumulator &CBA); + Optional initSectionHeaders(std::vector &SHeaders, + ContiguousBlobAccumulator &CBA); void initSymtabSectionHeader(Elf_Shdr &SHeader, SymtabType STType, ContiguousBlobAccumulator &CBA, ELFYAML::Section *YAMLSec); @@ -444,7 +451,9 @@ if (Doc.Header.EShNum) Header.e_shnum = *Doc.Header.EShNum; else if (!Doc.SectionHeaders || - (Doc.SectionHeaders->NoHeaders && !*Doc.SectionHeaders->NoHeaders)) + (Doc.SectionHeaders->NoHeaders && !*Doc.SectionHeaders->NoHeaders) || + (Doc.SectionHeaders->Location && !Doc.SectionHeaders->Sections && + !Doc.SectionHeaders->Excluded)) Header.e_shnum = Doc.getSections().size(); else if (!SHOff) Header.e_shnum = 0; @@ -663,23 +672,39 @@ return *Size; } +// Returns the offset of the section header table, or None when it is absent. template -void ELFState::initSectionHeaders(std::vector &SHeaders, - ContiguousBlobAccumulator &CBA) { +Optional +ELFState::initSectionHeaders(std::vector &SHeaders, + ContiguousBlobAccumulator &CBA) { // Ensure SHN_UNDEF entry is present. An all-zero section header is a // valid SHN_UNDEF entry since SHT_NULL == 0. SHeaders.resize(Doc.getSections().size()); - for (const std::unique_ptr &D : Doc.Chunks) { - if (ELFYAML::Fill *S = dyn_cast(D.get())) { + // In this method we initialize and write the section header table. + // We are going to iterate over all chunks to finalize each section header + // entry. When we need to place the section header table before the sections + // data we reserve the space for it. + Optional SHOff; + if (Doc.SectionHeaders && Doc.SectionHeaders->Location && + *Doc.SectionHeaders->Location == ELFYAML::SectionHeaderTable::BeforeSec) { + // If needed, align the start of the section header table. + SHOff = alignToOffset(CBA, sizeof(typename ELFT::uint), + /*Offset=*/None); + CBA.writeZeros(SHeaders.size() * sizeof(Elf_Shdr)); + } + + for (size_t I = 0, E = Doc.Chunks.size(); I != E; ++I) { + ELFYAML::Chunk *D = Doc.Chunks[I].get(); + if (ELFYAML::Fill *S = dyn_cast(D)) { S->Offset = alignToOffset(CBA, /*Align=*/1, S->Offset); writeFill(*S, CBA); LocationCounter += S->Size; continue; } - ELFYAML::Section *Sec = cast(D.get()); - bool IsFirstUndefSection = D == Doc.Chunks.front(); + ELFYAML::Section *Sec = cast(D); + bool IsFirstUndefSection = D == Doc.Chunks.front().get(); if (IsFirstUndefSection && Sec->IsImplicit) continue; @@ -782,6 +807,23 @@ // Override section fields if requested. overrideFields(Sec, SHeader); } + + if (!Doc.SectionHeaders || !Doc.SectionHeaders->NoHeaders.getValueOr(false)) { + if (SHOff) { + // We have already reserved the space for the section header table. + // Write it there. + CBA.writeAt(*SHOff, (char *)SHeaders.data(), + (uint64_t)SHeaders.size() * sizeof(Elf_Shdr)); + } else { + // If needed, align the start of the section header table. + SHOff = alignToOffset(CBA, sizeof(typename ELFT::uint), + /*Offset=*/None); + CBA.write((char *)SHeaders.data(), + (uint64_t)SHeaders.size() * sizeof(Elf_Shdr)); + } + } + + return SHOff; } template @@ -1766,6 +1808,10 @@ if (!Doc.SectionHeaders || Doc.SectionHeaders->NoHeaders) return DenseMap(); + if (Doc.SectionHeaders->Location && !Doc.SectionHeaders->Sections && + !Doc.SectionHeaders->Excluded) + return DenseMap(); + DenseMap Ret; size_t SecNdx = 0; StringSet<> Seen; @@ -1919,22 +1965,12 @@ ContiguousBlobAccumulator CBA(SectionContentBeginOffset, MaxSize); std::vector SHeaders; - State.initSectionHeaders(SHeaders, CBA); + Optional SHOff = State.initSectionHeaders(SHeaders, CBA); // Now we can decide segment offsets. State.setProgramHeaderLayout(PHeaders, SHeaders); - // If needed, align the start of the section header table, which is written - // after all section data. - const bool HasSectionHeaders = - !Doc.SectionHeaders || !Doc.SectionHeaders->NoHeaders.getValueOr(false); - Optional SHOff; - if (HasSectionHeaders) - SHOff = State.alignToOffset(CBA, sizeof(typename ELFT::uint), - /*Offset=*/None); - bool ReachedLimit = SHOff.getValueOr(CBA.getOffset()) + - arrayDataSize(makeArrayRef(SHeaders)) > - MaxSize; + bool ReachedLimit = CBA.getOffset() > MaxSize; if (Error E = CBA.takeLimitError()) { // We report a custom error message instead below. consumeError(std::move(E)); @@ -1952,8 +1988,6 @@ State.writeELFHeader(OS, SHOff); writeArrayData(OS, makeArrayRef(PHeaders)); CBA.writeBlobToStream(OS); - if (HasSectionHeaders) - writeArrayData(OS, makeArrayRef(SHeaders)); return true; } Index: llvm/lib/ObjectYAML/ELFYAML.cpp =================================================================== --- llvm/lib/ObjectYAML/ELFYAML.cpp +++ llvm/lib/ObjectYAML/ELFYAML.cpp @@ -28,6 +28,10 @@ ELFYAML::Chunk::~Chunk() = default; namespace ELFYAML { + +constexpr StringRef SectionHeaderTable::BeforeSec; +constexpr StringRef SectionHeaderTable::AfterSec; + unsigned Object::getMachine() const { if (Header.Machine) return *Header.Machine; @@ -865,13 +869,26 @@ IO.mapOptional("Sections", SectionHeader.Sections); IO.mapOptional("Excluded", SectionHeader.Excluded); IO.mapOptional("NoHeaders", SectionHeader.NoHeaders); + IO.mapOptional("Location", SectionHeader.Location); } std::string MappingTraits::validate( IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable) { + if (SecHdrTable.Location) { + if (SecHdrTable.NoHeaders.getValueOr(false)) + return "NoHeaders can't be set together with Location"; + if (*SecHdrTable.Location != ELFYAML::SectionHeaderTable::AfterSec && + *SecHdrTable.Location != ELFYAML::SectionHeaderTable::BeforeSec) + return ("Allowed values for Location are: " + + ELFYAML::SectionHeaderTable::BeforeSec + ", " + + ELFYAML::SectionHeaderTable::AfterSec) + .str(); + } + if (SecHdrTable.NoHeaders && (SecHdrTable.Sections || SecHdrTable.Excluded)) return "NoHeaders can't be used together with Sections/Excluded"; - if (!SecHdrTable.NoHeaders && !SecHdrTable.Sections && !SecHdrTable.Excluded) + if (!SecHdrTable.NoHeaders && !SecHdrTable.Sections && + !SecHdrTable.Excluded && !SecHdrTable.Location) return "SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop the " "section header table"; return ""; Index: llvm/test/tools/yaml2obj/ELF/section-headers-location.yaml =================================================================== --- /dev/null +++ llvm/test/tools/yaml2obj/ELF/section-headers-location.yaml @@ -0,0 +1,76 @@ +## Check how we can use the "Location" key of the "SectionHeaderTable" tag to +## specify where the section header table should be placed. + +## Check we can set the "Location" key to "AfterSecData" to place the section +## header table after all sections data. +## Check it is the default location for the section header table. +## Check we can use the "Location" key alone. + +# RUN: yaml2obj %s -o %t.after.1 +# RUN: llvm-readelf --file-headers --section-headers %t.after.1 | \ +# RUN: FileCheck %s --check-prefix=AFTER + +# RUN: yaml2obj -DLOC=AfterSecData %s -o %t.after.2 +# RUN: llvm-readelf --file-headers --section-headers %t.after.2 | \ +# RUN: FileCheck %s --check-prefix=AFTER + +# AFTER: Start of section headers: 96 (bytes into file) +# AFTER: Size of section headers: 64 (bytes) +# AFTER: Number of section headers: 4 + +## .shstrtab is the last section in the object. +## Start of section headers (96) == alignTo(0x41 + 0x18, /*Address size=*/8). +# AFTER: There are 4 section headers +# AFTER: [Nr] Name Type Address Off Size +# AFTER: [ 3] .shstrtab STRTAB [[#]] 000041 000018 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL +Sections: + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: + Location: [[LOC=AfterSecData]] + NoHeaders: [[NOHEADERS=]] + +## Check we do not allow using the "Location" key when the +## "NoHeaders" key is set to true. +# RUN: not yaml2obj %s -DNOHEADERS=true -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NO-HEADERS-ERR + +# NO-HEADERS-ERR: error: NoHeaders can't be set together with Location + +## Check we can use the "Location" key when the "NoHeaders" key is set to false. +# RUN: yaml2obj %s -DNOHEADERS=false -o %t.after.3 +# RUN: llvm-readelf --file-headers --section-headers %t.after.3 | \ +# RUN: FileCheck %s --check-prefix=AFTER + +## Check we can set the "Location" key to "BeforeSecData" to place the section +## header table before all sections data. + +# RUN: yaml2obj -DLOC=BeforeSecData %s -o %t.before.1 +# RUN: llvm-readelf --file-headers --section-headers %t.before.1 | \ +# RUN: FileCheck %s --check-prefix=BEFORE + +# BEFORE: Start of section headers: 64 (bytes into file) +# BEFORE: Size of section headers: 64 (bytes) +# BEFORE: Number of section headers: 4 + +## The first section is placed right after the section header table: +## Start of section headers (64) + Size of section headers (64) * +## Number of section headers (4) == 320 == 0x140. + +# BEFORE: [Nr] Name Type Address Off +# BEFORE: [ 1] .foo PROGBITS [[#]] 000140 + +## Check we don't allow any other values for the "Location" key. + +# RUN: not yaml2obj %s -DLOC=foo -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=UNKNOWN-ERR +# RUN: not yaml2obj %s -DLOC=0x123 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=UNKNOWN-ERR + +# UNKNOWN-ERR: error: Allowed values for Location are: BeforeSecData, AfterSecData