RawContent,
Relocation,
NoBits,
+ Hash,
Verdef,
Verneed,
StackSizes,
}
};
+struct HashSection : Section {
+ Optional<yaml::BinaryRef> Content;
+ Optional<std::vector<uint32_t>> Bucket;
+ Optional<std::vector<uint32_t>> Chain;
+
+ HashSection() : Section(SectionKind::Hash) {}
+
+ static bool classof(const Section *S) { return S->Kind == SectionKind::Hash; }
+};
+
struct VernauxEntry {
uint32_t Hash;
uint16_t Flags;
void writeSectionContent(Elf_Shdr &SHeader,
const ELFYAML::StackSizesSection &Section,
ContiguousBlobAccumulator &CBA);
+ void writeSectionContent(Elf_Shdr &SHeader,
+ const ELFYAML::HashSection &Section,
+ ContiguousBlobAccumulator &CBA);
ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH);
public:
} else if (auto S = dyn_cast<ELFYAML::VerdefSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::StackSizesSection>(Sec)) {
- writeSectionContent(SHeader, *S, CBA);
+ writeSectionContent(SHeader, *S, CBA);
+ } else if (auto S = dyn_cast<ELFYAML::HashSection>(Sec)) {
+ writeSectionContent(SHeader, *S, CBA);
} else {
llvm_unreachable("Unknown section type");
}
template <class ELFT>
void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
+ const ELFYAML::HashSection &Section,
+ ContiguousBlobAccumulator &CBA) {
+ raw_ostream &OS =
+ CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign);
+
+ unsigned Link = 0;
+ if (SN2I.lookup(".dynsym", Link))
+ SHeader.sh_link = Link;
+
+ if (Section.Content) {
+ SHeader.sh_size = writeContent(OS, Section.Content, None);
+ return;
+ }
+
+ support::endian::write<uint32_t>(OS, Section.Bucket->size(),
+ ELFT::TargetEndianness);
+ support::endian::write<uint32_t>(OS, Section.Chain->size(),
+ ELFT::TargetEndianness);
+ for (uint32_t Val : *Section.Bucket)
+ support::endian::write<uint32_t>(OS, Val, ELFT::TargetEndianness);
+ for (uint32_t Val : *Section.Chain)
+ support::endian::write<uint32_t>(OS, Val, ELFT::TargetEndianness);
+
+ SHeader.sh_size = (2 + Section.Bucket->size() + Section.Chain->size()) * 4;
+}
+
+template <class ELFT>
+void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
const ELFYAML::VerdefSection &Section,
ContiguousBlobAccumulator &CBA) {
typedef typename ELFT::Verdef Elf_Verdef;
IO.mapOptional("Entries", Section.Entries);
}
+static void sectionMapping(IO &IO, ELFYAML::HashSection &Section) {
+ commonSectionMapping(IO, Section);
+ IO.mapOptional("Content", Section.Content);
+ IO.mapOptional("Bucket", Section.Bucket);
+ IO.mapOptional("Chain", Section.Chain);
+}
+
static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) {
commonSectionMapping(IO, Section);
IO.mapOptional("Size", Section.Size, Hex64(0));
Section.reset(new ELFYAML::NoBitsSection());
sectionMapping(IO, *cast<ELFYAML::NoBitsSection>(Section.get()));
break;
+ case ELF::SHT_HASH:
+ if (!IO.outputting())
+ Section.reset(new ELFYAML::HashSection());
+ sectionMapping(IO, *cast<ELFYAML::HashSection>(Section.get()));
+ break;
case ELF::SHT_MIPS_ABIFLAGS:
if (!IO.outputting())
Section.reset(new ELFYAML::MipsABIFlags());
return ".stack_sizes: Content and Entries cannot be used together";
return {};
}
+
+ if (const auto *HS = dyn_cast<ELFYAML::HashSection>(Section.get())) {
+ if (!HS->Content && !HS->Bucket && !HS->Chain)
+ return "one of \"Content\", \"Bucket\" or \"Chain\" must be specified";
+
+ if (HS->Content) {
+ if (HS->Bucket)
+ return "\"Content\" and \"Bucket\" cannot be used together";
+ if (HS->Chain)
+ return "\"Content\" and \"Chain\" cannot be used together";
+ return {};
+ }
+
+ if ((HS->Bucket && !HS->Chain) || (!HS->Bucket && HS->Chain))
+ return "\"Bucket\" and \"Chain\" must be used together";
+ return {};
+ }
+
return {};
}
Info: progbits
- Name: hash
Type: SHT_HASH
+ Content: ''
- Name: dynamic
Type: SHT_DYNAMIC
- Name: note
Address: 0x20000
- Name: .hash
Type: SHT_HASH
+ Content: ''
ShSize: 0x40
Address: 0x10000
- Name: .dynamic
--- /dev/null
+## Check how obj2yaml produces SHT_HASH section descriptions.
+
+## Check that obj2yaml uses "Bucket" and "Chain" tags to describe
+## a SHT_HASH section when it has content of a correct size.
+## I.e. data size == 4 * (2 + nbucket + nchain).
+
+# RUN: yaml2obj --docnum=1 %s -o %t1
+# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=CHAIN-BUCKET
+
+# CHAIN-BUCKET: - Name: .hash1
+# CHAIN-BUCKET-NEXT: Type: SHT_HASH
+# CHAIN-BUCKET-NEXT: Bucket: [ 3 ]
+# CHAIN-BUCKET-NEXT: Chain: [ 4, 5 ]
+# CHAIN-BUCKET: - Name: .hash2
+# CHAIN-BUCKET-NEXT: Type: SHT_HASH
+# CHAIN-BUCKET-NEXT: Bucket: [ ]
+# CHAIN-BUCKET-NEXT: Chain: [ ]
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+## Case 1: A non-empty hash table: nbucket == 1, nchain == 2.
+ - Name: .hash1
+ Type: SHT_HASH
+ Content: '0100000002000000030000000400000005000000'
+## Case 2: An empty hash table: nbucket == 0, nchain == 0.
+ - Name: .hash2
+ Type: SHT_HASH
+ Content: '0000000000000000'
+
+## Check that obj2yaml falls back to using the "Content" tag when
+## hash sections are broken.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=CONTENT
+
+# CONTENT: - Name: .empty_hash
+# CONTENT-NEXT: Type: SHT_HASH
+# CONTENT-NEXT: Content: ''
+# CONTENT-NEXT: - Name: .invalid_header
+# CONTENT-NEXT: Type: SHT_HASH
+# CONTENT-NEXT: Content: '00'
+# CONTENT-NEXT: - Name: .truncated
+# CONTENT-NEXT: Type: SHT_HASH
+# CONTENT-NEXT: Content: '01000000020000000300000004000000'
+# CONTENT-NEXT: - Name: .oversized
+# CONTENT-NEXT: Type: SHT_HASH
+# CONTENT-NEXT: Content: '0100000002000000030000000400000000'
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+## Case 1: section has no data.
+ - Name: .empty_hash
+ Type: SHT_HASH
+ Content: ''
+## Case 2: section size is less than 2 * 4.
+ - Name: .invalid_header
+ Type: SHT_HASH
+ Content: '00'
+## Case 3: nbucket == 1, nchain == 2.
+## Section size is less than (2 * nbucket + nchain) * 4.
+ - Name: .truncated
+ Type: SHT_HASH
+ Content: '01000000020000000300000004000000'
+## Case 4: nbucket == 1, nchain == 2.
+## Section size is greater than (2 * nbucket + nchain) * 4.
+ - Name: .oversized
+ Type: SHT_HASH
+ Content: '0100000002000000030000000400000000'
--- /dev/null
+## Check how yaml2obj produces SHT_HASH sections.
+
+## Check we can describe a SHT_HASH section using the "Content" tag.
+
+# RUN: yaml2obj --docnum=1 %s -o %t1
+# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s --check-prefix=CONTENT
+
+# CONTENT: Name: .hash
+# CONTENT-NEXT: Type: SHT_HASH
+# CONTENT-NEXT: Flags [
+# CONTENT-NEXT: ]
+# CONTENT-NEXT: Address: 0x0
+# CONTENT-NEXT: Offset: 0x44
+# CONTENT-NEXT: Size: 20
+# CONTENT-NEXT: Link: 1
+# CONTENT-NEXT: Info: 0
+# CONTENT-NEXT: AddressAlignment: 0
+# CONTENT-NEXT: EntrySize: 0
+# CONTENT-NEXT: SectionData (
+# CONTENT-NEXT: 0000: 01000000 02000000 03000000 04000000
+# CONTENT-NEXT: 0010: 05000000
+# CONTENT-NEXT: )
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+## SHT_HASH is linked to dynamic symbol table by default.
+ - Name: .dynsym
+ Type: SHT_DYNSYM
+ - Name: .hash
+ Type: SHT_HASH
+ Content: '0100000002000000030000000400000005000000'
+
+## Check we can describe a SHT_HASH section using "Bucket" and "Chain" tags.
+
+# RUN: yaml2obj --docnum=2 %s -o %t2
+# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefix=BUCKET-CHAIN
+
+# BUCKET-CHAIN: Name: .hash
+# BUCKET-CHAIN: Size:
+# BUCKET-CHAIN-SAME: 28
+# BUCKET-CHAIN: Link:
+# BUCKET-CHAIN-SAME: 0
+# BUCKET-CHAIN: SectionData (
+# BUCKET-CHAIN-NEXT: 0000: 02000000 03000000 01000000 02000000 |
+# BUCKET-CHAIN-NEXT: 0010: 03000000 04000000 05000000 |
+# BUCKET-CHAIN-NEXT: )
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Bucket: [ 1, 2 ]
+ Chain: [ 3, 4, 5 ]
+
+## Check we can't use "Content" and "Bucket" tags together.
+
+# RUN: not yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=CONTENT-BUCKET
+
+# CONTENT-BUCKET: error: "Content" and "Bucket" cannot be used together
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Bucket: [ 1 ]
+ Content: '00'
+
+## Check we can't use "Content" and "Chain" tags together.
+
+# RUN: not yaml2obj --docnum=4 %s 2>&1 | FileCheck %s --check-prefix=CONTENT-CHAIN
+
+# CONTENT-CHAIN: error: "Content" and "Chain" cannot be used together
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Chain: [ 1 ]
+ Content: '00'
+
+## Check we can't use "Bucket" without "Chain".
+
+# RUN: not yaml2obj --docnum=5 %s 2>&1 | FileCheck %s --check-prefix=NO-BUCKET-OR-CHAIN
+
+# NO-BUCKET-OR-CHAIN: error: "Bucket" and "Chain" must be used together
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Bucket: [ 1 ]
+
+## Check we can't use "Chain" without "Bucket".
+
+# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=NO-BUCKET-OR-CHAIN
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
+ Chain: [ 1 ]
+
+## Check we report an error if neither "Bucket", "Chain" nor "Content" were set.
+
+# RUN: not yaml2obj --docnum=7 %s 2>&1 | FileCheck %s --check-prefix=NO-TAGS
+
+# NO-TAGS: error: one of "Content", "Bucket" or "Chain" must be specified
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS32
+ Data: ELFDATA2LSB
+ Type: ET_DYN
+ Machine: EM_386
+Sections:
+ - Name: .hash
+ Type: SHT_HASH
Expected<ELFYAML::SymtabShndxSection *>
dumpSymtabShndxSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::VerdefSection *> dumpVerdefSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::SymverSection *> dumpSymverSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::VerneedSection *> dumpVerneedSection(const Elf_Shdr *Shdr);
Y->Sections.emplace_back(*SecOrErr);
break;
}
+ case ELF::SHT_HASH: {
+ Expected<ELFYAML::HashSection *> SecOrErr = dumpHashSection(&Sec);
+ if (!SecOrErr)
+ return SecOrErr.takeError();
+ Y->Sections.emplace_back(*SecOrErr);
+ break;
+ }
case ELF::SHT_GNU_verdef: {
Expected<ELFYAML::VerdefSection *> SecOrErr = dumpVerdefSection(&Sec);
if (!SecOrErr)
}
template <class ELFT>
+Expected<ELFYAML::HashSection *>
+ELFDumper<ELFT>::dumpHashSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::HashSection>();
+ if (Error E = dumpCommonSection(Shdr, *S))
+ return std::move(E);
+
+ auto ContentOrErr = Obj.getSectionContents(Shdr);
+ if (!ContentOrErr)
+ return ContentOrErr.takeError();
+
+ ArrayRef<uint8_t> Content = *ContentOrErr;
+ if (Content.size() % 4 != 0 || Content.size() < 8) {
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ DataExtractor::Cursor Cur(0);
+ DataExtractor Data(Content, Obj.isLE(), /*AddressSize=*/0);
+ uint32_t NBucket = Data.getU32(Cur);
+ uint32_t NChain = Data.getU32(Cur);
+ if (Content.size() != (2 + NBucket + NChain) * 4) {
+ S->Content = yaml::BinaryRef(Content);
+ return S.release();
+ }
+
+ S->Bucket.emplace(NBucket);
+ for (uint32_t &V : *S->Bucket)
+ V = Data.getU32(Cur);
+
+ S->Chain.emplace(NChain);
+ for (uint32_t &V : *S->Chain)
+ V = Data.getU32(Cur);
+
+ if (!Cur)
+ llvm_unreachable("entries were not read correctly");
+ return S.release();
+}
+
+template <class ELFT>
Expected<ELFYAML::VerdefSection *>
ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) {
typedef typename ELFT::Verdef Elf_Verdef;