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 @@ -139,6 +139,7 @@ NoBits, Note, Hash, + GnuHash, Verdef, Verneed, StackSizes, @@ -249,6 +250,39 @@ static bool classof(const Section *S) { return S->Kind == SectionKind::Hash; } }; +struct GnuHashHeader { + // The number of hash buckets. + // Not used when dumping the object, but can be used to override + // the real number of buckets when emiting an object from a YAML document. + Optional NBuckets; + + // Index of the first symbol in the dynamic symbol table + // included in the hash table. + llvm::yaml::Hex32 SymNdx; + + // The number of words in the Bloom filter. + // Not used when dumping the object, but can be used to override the real + // number of words in the Bloom filter when emiting an object from a YAML + // document. + Optional MaskWords; + + // A shift constant used by the Bloom filter. + llvm::yaml::Hex32 Shift2; +}; + +struct GnuHashSection : Section { + Optional Content; + + Optional Header; + Optional> BloomFilter; + Optional> HashBuckets; + Optional> HashValues; + + GnuHashSection() : Section(SectionKind::GnuHash) {} + + static bool classof(const Section *S) { return S->Kind == SectionKind::GnuHash; } +}; + struct VernauxEntry { uint32_t Hash; uint16_t Flags; @@ -541,6 +575,10 @@ static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); }; +template <> struct MappingTraits { + static void mapping(IO &IO, ELFYAML::GnuHashHeader &Rel); +}; + template <> struct MappingTraits { static void mapping(IO &IO, ELFYAML::DynamicEntry &Rel); }; 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 @@ -182,6 +182,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::NoteSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::GnuHashSection &Section, + ContiguousBlobAccumulator &CBA); ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH); @@ -440,6 +443,8 @@ writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else { llvm_unreachable("Unknown section type"); } @@ -1091,6 +1096,70 @@ SHeader.sh_size = OS.tell() - Offset; } +template +void ELFState::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::GnuHashSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + unsigned Link = 0; + if (Section.Link.empty() && SN2I.lookup(".dynsym", Link)) + SHeader.sh_link = Link; + + if (Section.Content) { + SHeader.sh_size = writeContent(OS, Section.Content, None); + return; + } + + // We write the header first, starting with the hash buckets count. Normally + // it is the number of entries in HashBuckets, but the "NBuckets" property can + // be used to override this field, which is useful for producing broken + // objects. + if (Section.Header->NBuckets) + support::endian::write(OS, *Section.Header->NBuckets, + ELFT::TargetEndianness); + else + support::endian::write(OS, Section.HashBuckets->size(), + ELFT::TargetEndianness); + + // Write the index of the first symbol in the dynamic symbol table accessible + // via the hash table. + support::endian::write(OS, Section.Header->SymNdx, + ELFT::TargetEndianness); + + // Write the number of words in the Bloom filter. As above, the "MaskWords" + // property can be used to set this field to any value. + if (Section.Header->MaskWords) + support::endian::write(OS, *Section.Header->MaskWords, + ELFT::TargetEndianness); + else + support::endian::write(OS, Section.BloomFilter->size(), + ELFT::TargetEndianness); + + // Write the shift constant used by the Bloom filter. + support::endian::write(OS, Section.Header->Shift2, + ELFT::TargetEndianness); + + // We've finished writing the header. Now write the Bloom filter. + for (llvm::yaml::Hex64 Val : *Section.BloomFilter) + support::endian::write(OS, Val, + ELFT::TargetEndianness); + + // Write an array of hash buckets. + for (llvm::yaml::Hex32 Val : *Section.HashBuckets) + support::endian::write(OS, Val, ELFT::TargetEndianness); + + // Write an array of hash values. + for (llvm::yaml::Hex32 Val : *Section.HashValues) + support::endian::write(OS, Val, ELFT::TargetEndianness); + + SHeader.sh_size = 16 /*Header size*/ + + Section.BloomFilter->size() * sizeof(typename ELFT::uint) + + Section.HashBuckets->size() * 4 + + Section.HashValues->size() * 4; +} + template void ELFState::buildSectionIndex() { for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) { StringRef Name = Doc.Sections[I]->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 @@ -1039,6 +1039,15 @@ IO.mapOptional("Notes", Section.Notes); } + +static void sectionMapping(IO &IO, ELFYAML::GnuHashSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Header", Section.Header); + IO.mapOptional("BloomFilter", Section.BloomFilter); + IO.mapOptional("HashBuckets", Section.HashBuckets); + IO.mapOptional("HashValues", Section.HashValues); +} static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Size", Section.Size, Hex64(0)); @@ -1155,6 +1164,11 @@ Section.reset(new ELFYAML::NoteSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_GNU_HASH: + if (!IO.outputting()) + Section.reset(new ELFYAML::GnuHashSection()); + sectionMapping(IO, *cast(Section.get())); + break; case ELF::SHT_MIPS_ABIFLAGS: if (!IO.outputting()) Section.reset(new ELFYAML::MipsABIFlags()); @@ -1300,6 +1314,29 @@ return {}; } + if (const auto *Sec = dyn_cast(Section.get())) { + if (!Sec->Content && !Sec->Header && !Sec->BloomFilter && + !Sec->HashBuckets && !Sec->HashValues) + return "either \"Content\" or \"Header\", \"BloomFilter\", " + "\"HashBuckets\" and \"HashBuckets\" must be specified"; + + if (Sec->Header || Sec->BloomFilter || Sec->HashBuckets || + Sec->HashValues) { + if (!Sec->Header || !Sec->BloomFilter || !Sec->HashBuckets || + !Sec->HashValues) + return "\"Header\", \"BloomFilter\", " + "\"HashBuckets\" and \"HashValues\" must be used together"; + if (Sec->Content) + return "\"Header\", \"BloomFilter\", " + "\"HashBuckets\" and \"HashValues\" can't be used together with " + "\"Content\""; + return {}; + } + + // Only Content is specified. + return {}; + } + return {}; } @@ -1335,6 +1372,15 @@ IO.mapRequired("Size", E.Size); } +void MappingTraits::mapping(IO &IO, + ELFYAML::GnuHashHeader &E) { + assert(IO.getContext() && "The IO context is not initialized"); + IO.mapOptional("NBuckets", E.NBuckets); + IO.mapRequired("SymNdx", E.SymNdx); + IO.mapOptional("MaskWords", E.MaskWords); + IO.mapRequired("Shift2", E.Shift2); +} + void MappingTraits::mapping(IO &IO, ELFYAML::DynamicEntry &Rel) { assert(IO.getContext() && "The IO context is not initialized"); diff --git a/llvm/test/tools/llvm-readobj/elf-section-types.test b/llvm/test/tools/llvm-readobj/elf-section-types.test --- a/llvm/test/tools/llvm-readobj/elf-section-types.test +++ b/llvm/test/tools/llvm-readobj/elf-section-types.test @@ -210,6 +210,7 @@ Type: SHT_GNU_ATTRIBUTES - Name: gnu_hash Type: SHT_GNU_HASH + Content: "" - Name: gnu_verdef Type: SHT_GNU_verdef Info: 0 diff --git a/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml b/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml @@ -0,0 +1,132 @@ +## Check how obj2yaml produces SHT_GNU_HASH section descriptions. + +## Check that obj2yaml uses "Header", "BloomFilter", "HashBuckets" and "HashValues" +## tags to describe a SHT_GNU_HASH section when it has content of a correct size. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=FIELDS + +# FIELDS: - Name: .gnu.hash +# FIELDS-NEXT: Type: SHT_GNU_HASH +# FIELDS-NEXT: Flags: [ SHF_ALLOC ] +# FIELDS-NEXT: Header: +# FIELDS-NEXT: SymNdx: 0x00000001 +# FIELDS-NEXT: Shift2: 0x00000002 +# FIELDS-NEXT: BloomFilter: [ 0x0000000000000003, 0x0000000000000004 ] +# FIELDS-NEXT: HashBuckets: [ 0x00000005, 0x00000006, 0x00000007 ] +# FIELDS-NEXT: HashValues: [ 0x00000008, 0x00000009, 0x0000000A, 0x0000000B ] + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x1 + Shift2: 0x2 + BloomFilter: [0x3, 0x4] + HashBuckets: [0x5, 0x6, 0x7] + HashValues: [0x8, 0x9, 0xA, 0xB] + +## Check how we handle broken cases. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=INVALID + +# INVALID: - Name: .gnu.hash.tooshort +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Flags: [ SHF_ALLOC ] +# INVALID-NEXT: Content: 112233445566778899AABBCCDDEEFF +# INVALID-NEXT: - Name: .gnu.hash.empty +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Flags: [ SHF_ALLOC ] +# INVALID-NEXT: Header: +# INVALID-NEXT: SymNdx: 0x00000000 +# INVALID-NEXT: Shift2: 0x00000000 +# INVALID-NEXT: BloomFilter: [ ] +# INVALID-NEXT: HashBuckets: [ ] +# INVALID-NEXT: HashValues: [ ] +# INVALID-NEXT: - Name: .gnu.hash.broken.maskwords +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Content: '00000000000000000100000000000000' +# INVALID-NEXT: - Name: .gnu.hash.broken.nbuckets +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Content: '01000000000000000000000000000000' +# INVALID-NEXT: - Name: .gnu.hash.hashvalues.ok +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Header: +# INVALID-NEXT: SymNdx: 0x00000000 +# INVALID-NEXT: Shift2: 0x00000000 +# INVALID-NEXT: BloomFilter: [ ] +# INVALID-NEXT: HashBuckets: [ ] +# INVALID-NEXT: HashValues: [ 0x00000000 ] +# INVALID-NEXT: - Name: .gnu.hash.hashvalues.fail +# INVALID-NEXT: Type: SHT_GNU_HASH +# INVALID-NEXT: Content: '000000000000000000000000000000000000000000' + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: +## Case 1: Content is less than 16 bytes. + - Name: .gnu.hash.tooshort + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Content: "112233445566778899AABBCCDDEEFF" +## Case2: Check how we handle a fully empty hash section. +## It is almost technically valid, but uncommon. Modern linkers +## create at least one entry in Bloom filter if they want to disable it. +## Also, the dynamic symbol table has a null entry and having SymNdx = 0 +## here is at least strange. + - Name: .gnu.hash.empty + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x0 + Shift2: 0x0 + MaskWords: 0x0 + NBuckets: 0x0 + BloomFilter: [] + HashBuckets: [] + HashValues: [] +## Case 3: MaskWords field is broken: it says that the number of entries +## in the Bloom filter is 1, but the Bloom filter is empty. + - Name: .gnu.hash.broken.maskwords + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + Shift2: 0x0 + MaskWords: 0x1 + NBuckets: 0x0 + BloomFilter: [] + HashBuckets: [] + HashValues: [] +## Case 4: NBuckets field is broken, it says that the number of entries +## in the hash buckets is 1, but it is empty. + - Name: .gnu.hash.broken.nbuckets + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + Shift2: 0x0 + MaskWords: 0x0 + NBuckets: 0x1 + BloomFilter: [] + HashBuckets: [] + HashValues: [] +## Case 5: Check that we use the various properties to dump the data when it +## has a size that is a multiple of 4, but fallback to dumping the whole section +## using the "Content" property otherwise. + - Name: .gnu.hash.hashvalues.ok + Type: SHT_GNU_HASH + Content: "0000000000000000000000000000000000000000" + - Name: .gnu.hash.hashvalues.fail + Type: SHT_GNU_HASH + Content: "000000000000000000000000000000000000000000" diff --git a/llvm/test/tools/yaml2obj/elf-gnu-hash-section.yaml b/llvm/test/tools/yaml2obj/elf-gnu-hash-section.yaml new file mode 100644 --- /dev/null +++ b/llvm/test/tools/yaml2obj/elf-gnu-hash-section.yaml @@ -0,0 +1,288 @@ +## Check how yaml2obj produces SHT_GNU_HASH sections. + +## Check we can describe a SHT_GNU_HASH section using the "Content" tag. +## Check we set sh_link to index of the .dynsym by default. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s --check-prefix=CONTENT + +# CONTENT: Name: .gnu.hash +# CONTENT-NEXT: Type: SHT_GNU_HASH +# CONTENT-NEXT: Flags [ +# CONTENT-NEXT: ] +# CONTENT-NEXT: Address: 0x0 +# CONTENT-NEXT: Offset: 0x40 +# CONTENT-NEXT: Size: 3 +# CONTENT-NEXT: Link: 4 +# CONTENT-NEXT: Info: 0 +# CONTENT-NEXT: AddressAlignment: 0 +# CONTENT-NEXT: EntrySize: 0 +# CONTENT-NEXT: SectionData ( +# CONTENT-NEXT: 0000: 001122 | +# CONTENT-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Content: "001122" +## Used to trigger .dynsym creation. +DynamicSymbols: + - Name: foo + Binding: STB_GLOBAL + +## Check we can use "Header", "BloomFilter", "HashBuckets" and "HashValues" keys to describe +## the hash section. Check we can set sh_link to any arbitrary value. Check both ELFCLASS32 and 64 bit output. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: yaml2obj --docnum=3 %s -o %t3 +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefix=CONTENT32 +# RUN: llvm-readobj --sections --section-data %t3 | FileCheck %s --check-prefix=CONTENT64 + +# CONTENT32: Name: .gnu.hash +# CONTENT32-NEXT: Type: SHT_GNU_HASH +# CONTENT32-NEXT: Flags [ +# CONTENT32-NEXT: SHF_ALLOC +# CONTENT32-NEXT: ] +# CONTENT32-NEXT: Address: 0x0 +# CONTENT32-NEXT: Offset: 0x34 +# CONTENT32-NEXT: Size: 52 +# CONTENT32-NEXT: Link: 254 +# CONTENT32-NEXT: Info: 0 +# CONTENT32-NEXT: AddressAlignment: 0 +# CONTENT32-NEXT: EntrySize: 0 +# CONTENT32-NEXT: SectionData ( +# CONTENT32-NEXT: 0000: 03000000 01000000 02000000 02000000 | +# CONTENT32-NEXT: 0010: 03000000 04000000 05000000 06000000 | +# CONTENT32-NEXT: 0020: 07000000 08000000 09000000 0A000000 | +# CONTENT32-NEXT: 0030: 0B000000 | +# CONTENT32-NEXT: ) + +# CONTENT64: Name: .gnu.hash +# CONTENT64-NEXT: Type: SHT_GNU_HASH +# CONTENT64-NEXT: Flags [ +# CONTENT64-NEXT: SHF_ALLOC +# CONTENT64-NEXT: ] +# CONTENT64-NEXT: Address: 0x0 +# CONTENT64-NEXT: Offset: 0x40 +# CONTENT64-NEXT: Size: 60 +# CONTENT64-NEXT: Link: 254 +# CONTENT64-NEXT: Info: 0 +# CONTENT64-NEXT: AddressAlignment: 0 +# CONTENT64-NEXT: EntrySize: 0 +# CONTENT64-NEXT: SectionData ( +# CONTENT64-NEXT: 0000: 03000000 01000000 02000000 02000000 | +# CONTENT64-NEXT: 0010: 03000000 00000000 04000000 00000000 | +# CONTENT64-NEXT: 0020: 05000000 06000000 07000000 08000000 | +# CONTENT64-NEXT: 0030: 09000000 0A000000 0B000000 | +# CONTENT64-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x1 + Shift2: 0x2 + BloomFilter: [0x3, 0x4] + HashBuckets: [0x5, 0x6, 0x7] + HashValues: [0x8, 0x9, 0xA, 0xB] + Link: 0xFE + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Header: + SymNdx: 0x1 + Shift2: 0x2 + BloomFilter: [0x3, 0x4] + HashBuckets: [0x5, 0x6, 0x7] + HashValues: [0x8, 0x9, 0xA, 0xB] + Link: 0xFE + +## Check we only can use "Header", "BloomFilter", "HashBuckets" and "HashValues" together. + +# RUN: not yaml2obj --docnum=4 %s -o %t4 2>&1 | FileCheck %s --check-prefix=ERR +# RUN: not yaml2obj --docnum=5 %s -o %t5 2>&1 | FileCheck %s --check-prefix=ERR +# RUN: not yaml2obj --docnum=6 %s -o %t6 2>&1 | FileCheck %s --check-prefix=ERR +# RUN: not yaml2obj --docnum=7 %s -o %t7 2>&1 | FileCheck %s --check-prefix=ERR + +# ERR: error: "Header", "BloomFilter", "HashBuckets" and "HashValues" must be used together + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash.no.header + Type: SHT_GNU_HASH + BloomFilter: [] + HashBuckets: [] + HashValues: [] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash.no.bloomfilter + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + Shift2: 0x0 + HashBuckets: [] + HashValues: [] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash.no.nobuckets + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [] + HashValues: [] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash.no.novalues + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [] + HashBuckets: [] + +## Check that "SymNdx" and "Shift2" fields are mandatory when we specify the "Header". + +# RUN: not yaml2obj --docnum=8 %s -o %t8 2>&1 | FileCheck %s --check-prefix=ERR2 +# ERR2: error: missing required key 'SymNdx' + +# RUN: not yaml2obj --docnum=9 %s -o %t9 2>&1 | FileCheck %s --check-prefix=ERR3 +# ERR3: error: missing required key 'Shift2' + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Header: + Shift2: 0x0 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Header: + SymNdx: 0x0 + +## Either "Content" or "Header", "BloomFilter", "HashBuckets" and "HashBuckets" must be +## specified when declaring a SHT_GNU_HASH section. + +# RUN: not yaml2obj --docnum=10 %s -o %t10 2>&1 | FileCheck %s --check-prefix=NOKEYS + +# NOKEYS: error: either "Content" or "Header", "BloomFilter", "HashBuckets" and "HashBuckets" must be specified + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + +## Test that "Header", "BloomFilter", "HashBuckets" and "HashValues" can't be used together with "Content". + +# RUN: not yaml2obj --docnum=11 %s -o %t11 2>&1 | FileCheck %s --check-prefix=TOGETHER + +# TOGETHER: error: "Header", "BloomFilter", "HashBuckets" and "HashValues" can't be used together with "Content" + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Content: "" + Header: + SymNdx: 0x0 + Shift2: 0x0 + BloomFilter: [] + HashBuckets: [] + HashValues: [] + +## Test we can override the number of buckets and the number of words in the Bloom filter +## using the "NBuckets" and "Shift2" keys. + +# RUN: yaml2obj --docnum=12 %s -o %t12 +# RUN: llvm-readobj --sections --section-data %t12 | FileCheck %s --check-prefix=OVERRIDE-CONTENT + +# OVERRIDE-CONTENT: Name: .gnu.hash +# OVERRIDE-CONTENT: SectionData ( +# OVERRIDE-CONTENT-NEXT: 0000: 01000000 02000000 03000000 04000000 | +# OVERRIDE-CONTENT-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: +Sections: + - Name: .gnu.hash + Type: SHT_GNU_HASH + Header: + NBuckets: 0x1 + SymNdx: 0x2 + MaskWords: 0x3 + Shift2: 0x4 + BloomFilter: [] + HashBuckets: [] + HashValues: [] diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -69,6 +69,7 @@ Expected dumpNoBitsSection(const Elf_Shdr *Shdr); Expected dumpHashSection(const Elf_Shdr *Shdr); Expected dumpNoteSection(const Elf_Shdr *Shdr); + Expected dumpGnuHashSection(const Elf_Shdr *Shdr); Expected dumpVerdefSection(const Elf_Shdr *Shdr); Expected dumpSymverSection(const Elf_Shdr *Shdr); Expected dumpVerneedSection(const Elf_Shdr *Shdr); @@ -279,6 +280,13 @@ Y->Sections.emplace_back(*SecOrErr); break; } + case ELF::SHT_GNU_HASH: { + Expected SecOrErr = dumpGnuHashSection(&Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + Y->Sections.emplace_back(*SecOrErr); + break; + } case ELF::SHT_GNU_verdef: { Expected SecOrErr = dumpVerdefSection(&Sec); if (!SecOrErr) @@ -763,6 +771,57 @@ } template +Expected +ELFDumper::dumpGnuHashSection(const Elf_Shdr *Shdr) { + auto S = std::make_unique(); + if (Error E = dumpCommonSection(Shdr, *S)) + return std::move(E); + + auto ContentOrErr = Obj.getSectionContents(Shdr); + if (!ContentOrErr) + return ContentOrErr.takeError(); + + unsigned AddrSize = ELFT::Is64Bits ? 8 : 4; + ArrayRef Content = *ContentOrErr; + DataExtractor Data(Content, Obj.isLE(), AddrSize); + + ELFYAML::GnuHashHeader Header; + DataExtractor::Cursor Cur(0); + uint32_t NBuckets = Data.getU32(Cur); + Header.SymNdx = Data.getU32(Cur); + uint32_t MaskWords = Data.getU32(Cur); + Header.Shift2 = Data.getU32(Cur); + + // Set just the raw binary content if we were unable to read the header + // or when the section data is truncated or malformed. + uint64_t Size = Data.getData().size() - Cur.tell(); + if (!Cur || (Size < MaskWords * AddrSize + NBuckets * 4) || + (Size % 4 != 0)) { + consumeError(Cur.takeError()); + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + S->Header = Header; + + S->BloomFilter.emplace(MaskWords); + for (llvm::yaml::Hex64 &Val : *S->BloomFilter) + Val = Data.getAddress(Cur); + + S->HashBuckets.emplace(NBuckets); + for (llvm::yaml::Hex32 &Val : *S->HashBuckets) + Val = Data.getU32(Cur); + + S->HashValues.emplace((Data.getData().size() - Cur.tell()) / 4); + for (llvm::yaml::Hex32 &Val : *S->HashValues) + Val = Data.getU32(Cur); + + if (Cur) + return S.release(); + llvm_unreachable("GnuHashSection was not read correctly"); +} + +template Expected ELFDumper::dumpVerdefSection(const Elf_Shdr *Shdr) { typedef typename ELFT::Verdef Elf_Verdef;