[yaml2obj] - Introduce the "NoHeaders" key for "SectionHeaderTable"

We have an issue currently. The following YAML piece just ignores the `Excluded` key.

```
SectionHeaderTable:
  Sections: []
  Excluded:
    - Name: .foo
```

Currently the meaning is: exclude the whole table.

The code checks that the `Sections` key is empty and doesn't catch/check
invalid/duplicated/missed `Excluded` entries.

Also there is no way to exclude all sections except the first null section,
because `Sections: []` currently just excludes the whole the sections header table.

To fix it, I suggest a change of the behavior.

1) A new `NoHeaders` key is added. It provides an explicit syntax to drop the whole table.
2) The meaning of the following is changed:

```
SectionHeaderTable:
  Sections: []
  Excluded:
    - Name: .foo

```
Assuming there are 2 sections in the object (a null section and `.foo`), with this patch it
means: exclude the `.foo` section, keep the null section. The null section is an implicit
section and I think it is reasonable to make "Sections: []" to mean it is implicitly added.
It will be consistent with the global "Sections" tag that is used to describe sections.

3) `SectionHeaderTable->Sections` is now optional. No `Sections` is the same as
   `Sections: []` (I think it avoids a confusion).
4) Using of `NoHeaders` together with `Sections`/`Excluded` is not allowed.
5) It is possible to use the `Excluded` key without the `Sections` key now (in this case
   `Excluded` must contain all sections).
6) `SectionHeaderTable:` or `SectionHeaderTable: []` is not allowed.
7) When the `SectionHeaderTable` key is present, we still require all sections to be
   present in `Sections` and `Excluded` lists. No changes here, we are still strict.

Differential revision: https://reviews.llvm.org/D81655
This commit is contained in:
Georgii Rymar 2020-06-11 16:46:56 +03:00
parent 298377f4b0
commit ec4e68e667
5 changed files with 171 additions and 49 deletions

View File

@ -92,8 +92,9 @@ struct SectionHeader {
};
struct SectionHeaderTable {
std::vector<SectionHeader> Sections;
Optional<std::vector<SectionHeader>> Sections;
Optional<std::vector<SectionHeader>> Excluded;
bool NoHeaders;
};
struct SectionName {
@ -685,6 +686,7 @@ struct MappingTraits<ELFYAML::FileHeader> {
template <> struct MappingTraits<ELFYAML::SectionHeaderTable> {
static void mapping(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable);
static StringRef validate(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable);
};
template <> struct MappingTraits<ELFYAML::SectionHeader> {

View File

@ -402,9 +402,11 @@ void ELFState<ELFT>::writeELFHeader(raw_ostream &OS, uint64_t SHOff) {
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 @@ void ELFState<ELFT>::writeELFHeader(raw_ostream &OS, uint64_t SHOff) {
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 @@ unsigned ELFState<ELFT>::toSectionIndex(StringRef S, StringRef LocSec,
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 @@ void ELFState<ELFT>::writeFill(ELFYAML::Fill &Fill,
template <class ELFT>
DenseMap<StringRef, size_t> ELFState<ELFT>::buildSectionHeaderReorderMap() {
if (!Doc.SectionHeaders || Doc.SectionHeaders->Sections.empty())
if (!Doc.SectionHeaders || Doc.SectionHeaders->NoHeaders)
return DenseMap<StringRef, size_t>();
DenseMap<StringRef, size_t> Ret;
@ -1695,8 +1702,9 @@ DenseMap<StringRef, size_t> ELFState<ELFT>::buildSectionHeaderReorderMap() {
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 @@ template <class ELFT> void ELFState<ELFT>::buildSectionIndex() {
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<ELFYAML::Section *> 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);

View File

@ -839,8 +839,19 @@ void MappingTraits<ELFYAML::SectionHeader>::mapping(
void MappingTraits<ELFYAML::SectionHeaderTable>::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<ELFYAML::SectionHeaderTable>::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<ELFYAML::FileHeader>::mapping(IO &IO,

View File

@ -76,11 +76,17 @@ SectionHeaderTable:
- 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] <no-strings> NULL 0000000000000000 000000 000000 00 0 0 0
--- !ELF
FileHeader:
@ -92,6 +98,22 @@ SectionHeaderTable:
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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
- 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 @@ SectionHeaderTable:
- 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
- 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
## 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 @@ SectionHeaderTable:
- 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: []

View File

@ -79,9 +79,27 @@ SectionHeaderTable:
# 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 @@ Sections:
- 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 @@ Sections:
- 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