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 @@ -92,8 +92,9 @@ }; struct SectionHeaderTable { - std::vector Sections; + Optional> Sections; Optional> Excluded; + bool NoHeaders; }; struct SectionName { @@ -685,6 +686,7 @@ template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable); + static StringRef validate(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable); }; template <> struct MappingTraits { 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 @@ -402,9 +402,11 @@ Header.e_shentsize = Doc.Header.SHEntSize ? (uint16_t)*Doc.Header.SHEntSize : sizeof(Elf_Shdr); + const bool NoShdrs = Doc.SectionHeaders && Doc.SectionHeaders->NoHeaders; + if (Doc.Header.SHOff) Header.e_shoff = *Doc.Header.SHOff; - else if (Doc.SectionHeaders && Doc.SectionHeaders->Sections.empty()) + else if (NoShdrs) Header.e_shoff = 0; else Header.e_shoff = SHOff; @@ -413,18 +415,20 @@ Header.e_shnum = *Doc.Header.SHNum; else if (!Doc.SectionHeaders) Header.e_shnum = Doc.getSections().size(); - else if (Doc.SectionHeaders->Sections.empty()) + else if (NoShdrs) Header.e_shnum = 0; else - Header.e_shnum = Doc.SectionHeaders->Sections.size() + /*Null section*/ 1; + Header.e_shnum = + (Doc.SectionHeaders->Sections ? Doc.SectionHeaders->Sections->size() + : 0) + + /*Null section*/ 1; if (Doc.Header.SHStrNdx) Header.e_shstrndx = *Doc.Header.SHStrNdx; - else if ((!Doc.SectionHeaders || !Doc.SectionHeaders->Sections.empty()) && - !ExcludedSectionHeaders.count(".shstrtab")) - Header.e_shstrndx = SN2I.get(".shstrtab"); - else + else if (NoShdrs || ExcludedSectionHeaders.count(".shstrtab")) Header.e_shstrndx = 0; + else + Header.e_shstrndx = SN2I.get(".shstrtab"); OS.write((const char *)&Header, sizeof(Header)); } @@ -481,11 +485,14 @@ return 0; } - if (!Doc.SectionHeaders || !Doc.SectionHeaders->Excluded) + if (!Doc.SectionHeaders || + (!Doc.SectionHeaders->NoHeaders && !Doc.SectionHeaders->Excluded)) return Index; - assert(!Doc.SectionHeaders->Sections.empty()); - if (Index >= Doc.SectionHeaders->Sections.size()) { + assert(!Doc.SectionHeaders->NoHeaders || !Doc.SectionHeaders->Sections); + size_t FirstExcluded = + Doc.SectionHeaders->Sections ? Doc.SectionHeaders->Sections->size() : 0; + if (Index >= FirstExcluded) { if (LocSym.empty()) reportError("unable to link '" + LocSec + "' to excluded section '" + S + "'"); @@ -1681,7 +1688,7 @@ template DenseMap ELFState::buildSectionHeaderReorderMap() { - if (!Doc.SectionHeaders || Doc.SectionHeaders->Sections.empty()) + if (!Doc.SectionHeaders || Doc.SectionHeaders->NoHeaders) return DenseMap(); DenseMap Ret; @@ -1695,8 +1702,9 @@ Seen.insert(Hdr.Name); }; - for (const ELFYAML::SectionHeader &Hdr : Doc.SectionHeaders->Sections) - AddSection(Hdr); + if (Doc.SectionHeaders->Sections) + for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Sections) + AddSection(Hdr); if (Doc.SectionHeaders->Excluded) for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded) @@ -1727,13 +1735,21 @@ return; // Build excluded section headers map. - if (Doc.SectionHeaders && Doc.SectionHeaders->Excluded) - for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded) - if (!ExcludedSectionHeaders.insert(Hdr.Name).second) - llvm_unreachable("buildSectionIndex() failed"); + std::vector Sections = Doc.getSections(); + if (Doc.SectionHeaders) { + if (Doc.SectionHeaders->Excluded) + for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded) + if (!ExcludedSectionHeaders.insert(Hdr.Name).second) + llvm_unreachable("buildSectionIndex() failed"); + + if (Doc.SectionHeaders->NoHeaders) + for (const ELFYAML::Section *S : Sections) + if (!ExcludedSectionHeaders.insert(S->Name).second) + llvm_unreachable("buildSectionIndex() failed"); + } size_t SecNdx = -1; - for (const ELFYAML::Section *S : Doc.getSections()) { + for (const ELFYAML::Section *S : Sections) { ++SecNdx; size_t Index = ReorderMap.empty() ? SecNdx : ReorderMap.lookup(S->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 @@ -839,8 +839,19 @@ void MappingTraits::mapping( IO &IO, ELFYAML::SectionHeaderTable &SectionHeader) { - IO.mapRequired("Sections", SectionHeader.Sections); + IO.mapOptional("Sections", SectionHeader.Sections); IO.mapOptional("Excluded", SectionHeader.Excluded); + IO.mapOptional("NoHeaders", SectionHeader.NoHeaders, false); +} + +StringRef MappingTraits::validate( + IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable) { + if (SecHdrTable.NoHeaders && (SecHdrTable.Sections || SecHdrTable.Excluded)) + return "NoHeaders can't be used together with Sections/Excluded"; + if (!SecHdrTable.NoHeaders && !SecHdrTable.Sections && !SecHdrTable.Excluded) + return "SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop the " + "section header table"; + return StringRef(); } void MappingTraits::mapping(IO &IO, diff --git a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml --- a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml +++ b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml @@ -76,11 +76,17 @@ - Name: .strtab - Name: .strtab -## Check we are able to exclude all sections. +## Check that we are able to exclude all sections, except the implicit +## null section, with the use of the "Excluded" key. + +## Case A: the "Sections" key is present, but empty. # RUN: yaml2obj %s --docnum=3 -o %t3 # RUN: llvm-readelf --section-headers %t3 | FileCheck %s --check-prefix=NO-SECTIONS -# NO-SECTIONS: There are 0 section headers, starting at offset 0x0: +# NO-SECTIONS: There are 1 section headers, starting at offset 0x48: +# NO-SECTIONS: Section Headers: +# NO-SECTIONS-NEXT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# NO-SECTIONS-NEXT: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 --- !ELF FileHeader: @@ -92,6 +98,22 @@ Sections: [] Excluded: - Name: .strtab + - Name: .shstrtab + +## Case B: the "Sections" key is not present. +# RUN: yaml2obj %s --docnum=4 -o %t4 +# RUN: llvm-readelf --section-headers %t4 | FileCheck %s --check-prefix=NO-SECTIONS + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +SectionHeaderTable: + Excluded: + - Name: .strtab + - Name: .shstrtab ## Check how we handle cases when a section is excluded, but its section index is needed. ## The general rule is: when a section is explicitly linked with another section, which is @@ -100,7 +122,7 @@ ## Case A: check we report an error when a regular section has a Link field which ## points to an excluded section. -# RUN: not yaml2obj %s --docnum=4 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=5 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.foo -DTARGET=.bar # LINK: error: unable to link '[[SEC]]' to excluded section '[[TARGET]]' @@ -127,9 +149,9 @@ ## Case B.1: check we report an error when a symbol table section has a Link field which ## points to an excluded section. -# RUN: not yaml2obj %s --docnum=5 -DNAME=.symtab -DTYPE=SHT_SYMTAB -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=6 -DNAME=.symtab -DTYPE=SHT_SYMTAB -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.symtab -DTARGET=.foo -# RUN: not yaml2obj %s --docnum=5 -DNAME=.dynsym -DTYPE=SHT_DYNSYM -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=6 -DNAME=.dynsym -DTYPE=SHT_DYNSYM -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.dynsym -DTARGET=.foo --- !ELF @@ -153,8 +175,8 @@ - Name: .foo ## Case B.2: check we do not link .dynsym with .dynstr implicitly when the latter is excluded. -# RUN: yaml2obj %s --docnum=6 -o %t4 -# RUN: llvm-readelf %t4 --section-headers | FileCheck %s --check-prefix=LINK-DYNSYM +# RUN: yaml2obj %s --docnum=7 -o %t5 +# RUN: llvm-readelf %t5 --section-headers | FileCheck %s --check-prefix=LINK-DYNSYM # LINK-DYNSYM: [Nr] Name Type Address Off Size ES Flg Lk # LINK-DYNSYM: [ 1] .dynsym DYNSYM 0000000000000000 000040 000018 18 A 0 @@ -179,8 +201,8 @@ - Name: .dynstr ## Case B.3: check we do not link .symtab with .strtab implicitly when the latter is excluded. -# RUN: yaml2obj %s --docnum=7 -o %t5 -# RUN: llvm-readelf %t5 --section-headers | FileCheck %s --check-prefix=LINK-SYMTAB +# RUN: yaml2obj %s --docnum=8 -o %t6 +# RUN: llvm-readelf %t6 --section-headers | FileCheck %s --check-prefix=LINK-SYMTAB # LINK-SYMTAB: [Nr] Name Type Address Off Size ES Flg Lk Inf Al # LINK-SYMTAB: [ 1] .symtab SYMTAB 0000000000000000 000040 000018 18 0 1 0 @@ -205,7 +227,7 @@ ## Case C: check we report an error when a debug section has a Link field which ## points to an excluded section. -# RUN: not yaml2obj %s --docnum=8 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=9 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.debug_unknown -DTARGET=.strtab --- !ELF @@ -227,7 +249,7 @@ ## Case D.1: check we report an error when a relocatable section has an Info field which ## points to an excluded section. -# RUN: not yaml2obj %s --docnum=9 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=10 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.rela -DTARGET=.strtab --- !ELF @@ -250,7 +272,7 @@ ## Case D.2: check we report an error when the SHT_REL[A] section is linked ## with an excluded section explicitly. -# RUN: not yaml2obj %s --docnum=10 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=11 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.rela -DTARGET=.symtab --- !ELF @@ -275,7 +297,7 @@ - Name: .symtab ## Case E: check we report an error when a symbol references an excluded section. -# RUN: not yaml2obj %s --docnum=11 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=12 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=SYMBOL-SECTION # SYMBOL-SECTION: error: excluded section referenced: '.foo' by symbol 'foo' @@ -303,7 +325,7 @@ ## Case F.1: check we report an error when a group section ## contains an excluded section member. -# RUN: not yaml2obj %s --docnum=12 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=13 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.group -DTARGET=.strtab --- !ELF @@ -326,7 +348,7 @@ ## Case F.2: check we report an error when the group section is linked ## to an excluded section explicitly. -# RUN: not yaml2obj %s --docnum=13 -o /dev/null 2>&1 | \ +# RUN: not yaml2obj %s --docnum=14 -o /dev/null 2>&1 | \ # RUN: FileCheck %s --check-prefix=LINK -DSEC=.group -DTARGET=.symtab --- !ELF @@ -352,8 +374,8 @@ ## Case G: check we do not link SHT_LLVM_CALL_GRAPH_PROFILE/SHT_LLVM_ADDRSIG/SHT_GROUP/SHT_REL[A] sections ## with .symtab implicitly when the latter is excluded. -# RUN: yaml2obj %s --docnum=14 -o %t6 -# RUN: llvm-readelf %t6 --section-headers | FileCheck %s --check-prefix=LINK-IMPLICIT +# RUN: yaml2obj %s --docnum=15 -o %t7 +# RUN: llvm-readelf %t7 --section-headers | FileCheck %s --check-prefix=LINK-IMPLICIT # LINK-IMPLICIT: [Nr] Name Type Address Off Size ES Flg Lk Inf Al # LINK-IMPLICIT: [ 1] .cgp LLVM_CALL_GRAPH_PROFILE 0000000000000000 000040 000000 10 0 0 0 @@ -395,8 +417,8 @@ ## Case H: check we do not link SHT_HASH/SHT_GNU_HASH sections with .dynsym ## implicitly when the latter is excluded. -# RUN: yaml2obj %s --docnum=15 -o %t7 -# RUN: llvm-readelf %t7 --section-headers | FileCheck %s --check-prefix=LINK-HASH +# RUN: yaml2obj %s --docnum=16 -o %t8 +# RUN: llvm-readelf %t8 --section-headers | FileCheck %s --check-prefix=LINK-HASH # LINK-HASH: [Nr] Name Type Address Off Size ES Flg Lk Inf Al # LINK-HASH: [ 1] .hash HASH 0000000000000000 000040 000000 00 0 0 0 @@ -431,7 +453,7 @@ ## 1) It is a reasonable behavior, as it is perhaps usually a result of a mistake ## in a YAML description. ## 2) Helps to keep the code simpler. -# RUN: not yaml2obj %s --docnum=16 -o /dev/null 2>&1 | FileCheck %s --check-prefix=CROSS-LINK +# RUN: not yaml2obj %s --docnum=17 -o /dev/null 2>&1 | FileCheck %s --check-prefix=CROSS-LINK # CROSS-LINK: error: unable to link '.foo' to excluded section '.bar' # CROSS-LINK-NEXT: error: unable to link '.bar' to excluded section '.foo' @@ -459,7 +481,7 @@ ## Check we set e_shstrndx field to 0 when the section header string table is excluded. ## Check that the e_shnum field is adjusted properly when a section is removed. -# RUN: yaml2obj --docnum=17 %s -o %t9 +# RUN: yaml2obj --docnum=18 %s -o %t9 # RUN: llvm-readelf --file-headers %t9 | FileCheck %s --check-prefix=SHSTRTAB # SHSTRTAB: Number of section headers: 2 @@ -476,3 +498,17 @@ - Name: .strtab Excluded: - Name: .shstrtab + +## Check we do not allow using "Excluded" together with "NoHeaders". +# RUN: not yaml2obj %s --docnum=19 -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOHEADERS +# NOHEADERS: NoHeaders can't be used together with Sections/Excluded + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +SectionHeaderTable: + NoHeaders: true + Excluded: [] diff --git a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml --- a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml +++ b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml @@ -79,9 +79,27 @@ # ERR2-NEXT: error: section '.section.foo' should be present in the 'Sections' or 'Excluded' lists # 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 +## Check that when the an empty "Sections" list is used, we do not create an empty section header table. +# RUN: not yaml2obj %s --docnum=2 -o /dev/null 2>&1 | FileCheck %s --check-prefix=EMPTY-SECTIONS + +# EMPTY-SECTIONS: error: section '.foo' should be present in the 'Sections' or 'Excluded' lists +# EMPTY-SECTIONS-NEXT: error: section '.strtab' should be present in the 'Sections' or 'Excluded' lists +# EMPTY-SECTIONS-NEXT: error: section '.shstrtab' should be present in the 'Sections' or 'Excluded' lists + +--- !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 able to use "NoHeaders" property to produce an empty section header table. +# RUN: yaml2obj %s --docnum=3 -o %t3 # RUN: llvm-readelf --file-headers %t3 | FileCheck %s --check-prefix=NO-HEADERS # NO-HEADERS: Start of section headers: 0 (bytes into file) @@ -99,11 +117,50 @@ - Name: .foo Type: SHT_PROGBITS SectionHeaderTable: - Sections: [] + NoHeaders: true + +## Check we do not allow using "Sections" together with "NoHeaders". +# RUN: not yaml2obj %s --docnum=4 -o /dev/null 2>&1 | FileCheck %s --check-prefix=SECTIONS-NO-HEADERS + +# SECTIONS-NO-HEADERS: error: NoHeaders can't be used together with Sections/Excluded + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: + Sections: [] + NoHeaders: true + +## Check that we do not allow an empty SectionHeaderTable tag and suggest to use an explicit syntax instead. +# RUN: not yaml2obj %s --docnum=5 -DVAL="" -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO-VALUE + +# NO-VALUE: SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop the section header table + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS +SectionHeaderTable: [[VAL]] + +## An empty mapping is also not allowed for the SectionHeaderTable tag. +# RUN: not yaml2obj %s --docnum=5 -DVAL="[]" -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOT-A-MAPPING + +# NOT-A-MAPPING: error: not a mapping ## 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: yaml2obj %s --docnum=6 -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) @@ -123,13 +180,13 @@ - Name: .foo Type: SHT_PROGBITS SectionHeaderTable: - Sections: [] + NoHeaders: true ## 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: yaml2obj %s --docnum=7 -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: yaml2obj %s --docnum=7 -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