diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -86,6 +86,14 @@ Optional SHStrNdx; }; +struct SectionHeader { + StringRef Name; +}; + +struct SectionHeaderTable { + std::vector Sections; +}; + struct SectionName { StringRef Section; }; @@ -508,6 +516,7 @@ struct Object { FileHeader Header; + Optional SectionHeaders; std::vector ProgramHeaders; // An object might contain output section descriptions as well as @@ -539,6 +548,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::CallGraphEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::NoteEntry) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::ProgramHeader) +LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionHeader) LLVM_YAML_IS_SEQUENCE_VECTOR(std::unique_ptr) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::Symbol) LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::VerdefEntry) @@ -670,6 +680,14 @@ static void mapping(IO &IO, ELFYAML::FileHeader &FileHdr); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable); +}; + +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::SectionHeader &SHdr); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::ProgramHeader &FileHdr); }; diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -218,6 +218,8 @@ void assignSectionAddress(Elf_Shdr &SHeader, ELFYAML::Section *YAMLSec); + DenseMap buildSectionHeaderReorderMap(); + BumpPtrAllocator StringAlloc; uint64_t alignToOffset(ContiguousBlobAccumulator &CBA, uint64_t Align, llvm::Optional Offset); @@ -318,12 +320,29 @@ // other sections to the end of the file. uint64_t SHOff = alignToOffset(CBA, sizeof(typename ELFT::uint), /*Offset=*/None); - Header.e_shoff = - Doc.Header.SHOff ? typename ELFT::uint(*Doc.Header.SHOff) : SHOff; - Header.e_shnum = - Doc.Header.SHNum ? (uint16_t)*Doc.Header.SHNum : Doc.getSections().size(); - Header.e_shstrndx = Doc.Header.SHStrNdx ? (uint16_t)*Doc.Header.SHStrNdx - : SN2I.get(".shstrtab"); + + if (Doc.Header.SHOff) + Header.e_shoff = *Doc.Header.SHOff; + else if (Doc.SectionHeaders && Doc.SectionHeaders->Sections.empty()) + Header.e_shoff = 0; + else + Header.e_shoff = SHOff; + + if (Doc.Header.SHNum) + Header.e_shnum = *Doc.Header.SHNum; + else if (!Doc.SectionHeaders) + Header.e_shnum = Doc.getSections().size(); + else if (Doc.SectionHeaders->Sections.empty()) + Header.e_shnum = 0; + else + Header.e_shnum = Doc.SectionHeaders->Sections.size() + /*Null section*/ 1; + + if (Doc.Header.SHStrNdx) + Header.e_shstrndx = *Doc.Header.SHStrNdx; + else if (!Doc.SectionHeaders || !Doc.SectionHeaders->Sections.empty()) + Header.e_shstrndx = SN2I.get(".shstrtab"); + else + Header.e_shstrndx = 0; OS.write((const char *)&Header, sizeof(Header)); } @@ -1447,14 +1466,50 @@ Fill.Pattern->writeAsBinary(OS, Fill.Size - Written); } +template +DenseMap ELFState::buildSectionHeaderReorderMap() { + if (!Doc.SectionHeaders || Doc.SectionHeaders->Sections.empty()) + return DenseMap(); + + DenseMap Ret; + size_t SecNdx = 0; + StringSet<> Seen; + for (const ELFYAML::SectionHeader &Hdr : Doc.SectionHeaders->Sections) { + if (!Ret.try_emplace(Hdr.Name, ++SecNdx).second) + reportError("repeated section name: '" + Hdr.Name + + "' in the section header description"); + Seen.insert(Hdr.Name); + } + + for (const ELFYAML::Section *S : Doc.getSections()) { + // Ignore special first SHT_NULL section. + if (S == Doc.getSections().front()) + continue; + if (!Seen.count(S->Name)) + reportError("section '" + S->Name + + "' should be present in the 'Sections' list"); + Seen.erase(S->Name); + } + + for (const auto &It : Seen) + reportError("section header contains undefined section '" + It.getKey() + + "'"); + return Ret; +} + template void ELFState::buildSectionIndex() { + // A YAML description can have an explicit section header declaration that allows + // to change the order of section headers. + DenseMap ReorderMap = buildSectionHeaderReorderMap(); + size_t SecNdx = -1; for (const std::unique_ptr &C : Doc.Chunks) { if (!isa(C.get())) continue; ++SecNdx; - if (!SN2I.addName(C->Name, SecNdx)) + size_t Index = ReorderMap.empty() ? SecNdx : ReorderMap.lookup(C->Name); + if (!SN2I.addName(C->Name, Index)) llvm_unreachable("buildSectionIndex() failed"); DotShStrtab.add(ELFYAML::dropUniqueSuffix(C->Name)); } diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -832,6 +832,16 @@ #undef BCase } +void MappingTraits::mapping( + IO &IO, ELFYAML::SectionHeader &SHdr) { + IO.mapRequired("Name", SHdr.Name); +} + +void MappingTraits::mapping( + IO &IO, ELFYAML::SectionHeaderTable &SectionHeader) { + IO.mapRequired("Sections", SectionHeader.Sections); +} + void MappingTraits::mapping(IO &IO, ELFYAML::FileHeader &FileHdr) { IO.mapRequired("Class", FileHdr.Class); @@ -1638,6 +1648,7 @@ IO.setContext(&Object); IO.mapTag("!ELF", true); IO.mapRequired("FileHeader", Object.Header); + IO.mapOptional("SectionHeaderTable", Object.SectionHeaders); IO.mapOptional("ProgramHeaders", Object.ProgramHeaders); IO.mapOptional("Sections", Object.Chunks); IO.mapOptional("Symbols", Object.Symbols); diff --git a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml @@ -0,0 +1,184 @@ +## Check we can use "SectionHeaderTable" tag to reorder section header entries. + +## This is a general test that has sections with unique prefixes, a fill and a +## section without the unique prefix. The section header table describes sections +## in the same order they are listed in the YAML. +# RUN: yaml2obj %s --docnum=1 -o %t1 -DSEC1=".section (1)" -DSEC2=".section (2)" -DSEC3=".section.foo" +# RUN: llvm-readelf --section-headers %t1 | FileCheck %s --check-prefix=NO-OP + +# NO-OP: Section Headers: +# NO-OP-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# NO-OP-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# NO-OP-NEXT: [ 1] .section PROGBITS 0000000000000000 000040 000010 00 0 0 0 +# NO-OP-NEXT: [ 2] .section PROGBITS 0000000000000000 000050 000020 00 0 0 0 +# NO-OP-NEXT: [ 3] .section.foo PROGBITS 0000000000000000 0000a0 000040 00 0 0 0 +# NO-OP-NEXT: [ 4] .strtab STRTAB 0000000000000000 0000e0 000001 00 0 0 1 +# NO-OP-NEXT: [ 5] .shstrtab STRTAB 0000000000000000 0000e1 000029 00 0 0 1 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .section (1) + Type: SHT_PROGBITS + Size: 0x10 + - Name: .section (2) + Type: SHT_PROGBITS + Size: 0x20 + - Type: Fill + Name: .filler + Size: 0x30 + Pattern: "" + - Name: .section.foo + Type: SHT_PROGBITS + Size: 0x40 +SectionHeaderTable: + Sections: + - Name: [[SEC1]] + - Name: [[SEC2]] + - Name: [[SEC3]] + - Name: .strtab + - Name: .shstrtab + +## Show we are able to reorder sections. +# RUN: yaml2obj %s -o %t2 -DSEC3=".section (1)" -DSEC2=".section (2)" -DSEC1=".section.foo" +# RUN: llvm-readelf --section-headers %t2 | FileCheck %s --check-prefix=REORDERED + +# REORDERED: Section Headers: +# REORDERED-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# REORDERED-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# REORDERED-NEXT: [ 1] .section.foo PROGBITS 0000000000000000 0000a0 000040 00 0 0 0 +# REORDERED-NEXT: [ 2] .section PROGBITS 0000000000000000 000050 000020 00 0 0 0 +# REORDERED-NEXT: [ 3] .section PROGBITS 0000000000000000 000040 000010 00 0 0 0 +# REORDERED-NEXT: [ 4] .strtab STRTAB 0000000000000000 0000e0 000001 00 0 0 1 +# REORDERED-NEXT: [ 5] .shstrtab STRTAB 0000000000000000 0000e1 000029 00 0 0 1 + +## Show we report proper errors when the section header description: +## a) contains a repeated section name. +## b) omits any section that exists. +## c) contains a non-existent section. +# RUN: not yaml2obj %s -o /dev/null -DSEC1=".section.foo" -DSEC2="unknown" -DSEC3=".section.foo" 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR1 +# d) contains a repeated implicit section name. +# e) contains a fill name. +# RUN: not yaml2obj %s -o /dev/null -DSEC1=".strtab" -DSEC2=".shstrtab" -DSEC3=".filler" 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERR2 + +# ERR1: error: repeated section name: '.section.foo' in the section header description +# ERR1-NEXT: error: section '.section (1)' should be present in the 'Sections' list +# ERR1-NEXT: error: section '.section (2)' should be present in the 'Sections' list +# ERR1-NEXT: error: section header contains undefined section 'unknown' + +# ERR2: error: repeated section name: '.strtab' in the section header description +# ERR2-NEXT: error: repeated section name: '.shstrtab' in the section header description +# ERR2-NEXT: error: section '.section (1)' should be present in the 'Sections' list +# ERR2-NEXT: error: section '.section (2)' should be present in the 'Sections' list +# ERR2-NEXT: error: section '.section.foo' should be present in the 'Sections' list +# ERR2-NEXT: error: section header contains undefined section '.filler' + +## Test that we are able to specify an empty sections list for +## the "SectionHeaderTable" tag to produce no section header. +# RUN: yaml2obj %s --docnum=2 -o %t3 +# RUN: llvm-readelf --file-headers %t3 | FileCheck %s --check-prefix=NO-HEADERS + +# NO-HEADERS: Start of section headers: 0 (bytes into file) +# NO-HEADERS: Size of section headers: 64 (bytes) +# NO-HEADERS: Number of section headers: 0 +# NO-HEADERS: Section header string table index: 0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: [] + +## Test that we are still able to override e_shoff, e_shnum and e_shstrndx +## fields even when we do not produce section headers. +# RUN: yaml2obj %s --docnum=3 -o %t4 +# RUN: llvm-readelf --file-headers %t4 | FileCheck %s --check-prefix=NO-HEADERS-OVERRIDE + +# NO-HEADERS-OVERRIDE: Start of section headers: 2 (bytes into file) +# NO-HEADERS-OVERRIDE: Number of section headers: 3 +# NO-HEADERS-OVERRIDE: Section header string table index: 4 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 + SHOff: 0x2 + SHNum: 0x3 + SHStrNdx: 0x4 +Sections: + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: [] + +## Check that section indices are updated properly in other places when we +## reorder sections in the section header table. +# RUN: yaml2obj %s --docnum=4 -o %t5 -DSEC1=".foo" -DSEC2=".bar" +# RUN: llvm-readelf --section-headers --symbols %t5 | FileCheck %s --check-prefix=INDICES-A +# RUN: yaml2obj %s --docnum=4 -o %t6 -DSEC2=".foo" -DSEC1=".bar" +# RUN: llvm-readelf --section-headers --symbols %t6 | FileCheck %s --check-prefix=INDICES-B + +# INDICES-A: [Nr] Name Type Address Off Size ES Flg Lk +# INDICES-A: [ 1] .foo PROGBITS 0000000000000000 000040 000000 00 0 +# INDICES-A-NEXT: [ 2] .bar PROGBITS 0000000000000000 000040 000000 00 0 +# INDICES-A-NEXT: [ 3] .another.1 PROGBITS 0000000000000000 000040 000000 00 1 +# INDICES-A-NEXT: [ 4] .another.2 PROGBITS 0000000000000000 000040 000000 00 2 + +# INDICES-A: Num: Value Size Type Bind Vis Ndx Name +# INDICES-A: 1: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 foo +# INDICES-A-NEXT: 2: 0000000000000000 0 NOTYPE LOCAL DEFAULT 2 bar + +# INDICES-B: [ 1] .bar PROGBITS 0000000000000000 000040 000000 00 0 +# INDICES-B-NEXT: [ 2] .foo PROGBITS 0000000000000000 000040 000000 00 0 +# INDICES-B-NEXT: [ 3] .another.1 PROGBITS 0000000000000000 000040 000000 00 2 +# INDICES-B-NEXT: [ 4] .another.2 PROGBITS 0000000000000000 000040 000000 00 1 + +# INDICES-B: Num: Value Size Type Bind Vis Ndx Name +# INDICES-B: 1: 0000000000000000 0 NOTYPE LOCAL DEFAULT 2 foo +# INDICES-B-NEXT: 2: 0000000000000000 0 NOTYPE LOCAL DEFAULT 1 bar + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + - Name: .bar + Type: SHT_PROGBITS + - Name: .another.1 + Link: .foo + Type: SHT_PROGBITS + - Name: .another.2 + Link: .bar + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: + - Name: [[SEC1]] + - Name: [[SEC2]] + - Name: .another.1 + - Name: .another.2 + - Name: .symtab + - Name: .strtab + - Name: .shstrtab +Symbols: + - Name: foo + Section: .foo + - Name: bar + Section: .bar