[llvm] a7aee6c - [yaml2obj/obj2yaml] - Add support for SHT_GNU_HASH section.
Georgii Rymar via llvm-commits
llvm-commits at lists.llvm.org
Thu Oct 31 02:37:21 PDT 2019
Author: georgerim
Date: 2019-10-31T12:25:37+03:00
New Revision: a7aee6c47a1b12dd7d579b4f98d7049eb1e987ce
URL: https://github.com/llvm/llvm-project/commit/a7aee6c47a1b12dd7d579b4f98d7049eb1e987ce
DIFF: https://github.com/llvm/llvm-project/commit/a7aee6c47a1b12dd7d579b4f98d7049eb1e987ce.diff
LOG: [yaml2obj/obj2yaml] - Add support for SHT_GNU_HASH section.
This adds parsing and dumping support for GNU hash sections.
They are described nicely here: https://blogs.oracle.com/solaris/gnu-hash-elf-sections-v2
Differential revision: https://reviews.llvm.org/D69399
Added:
llvm/test/tools/obj2yaml/elf-gnu-hash-section.yaml
llvm/test/tools/yaml2obj/elf-gnu-hash-section.yaml
Modified:
llvm/include/llvm/ObjectYAML/ELFYAML.h
llvm/lib/ObjectYAML/ELFEmitter.cpp
llvm/lib/ObjectYAML/ELFYAML.cpp
llvm/test/tools/llvm-readobj/elf-section-types.test
llvm/tools/obj2yaml/elf2yaml.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h
index 8cd12b3f7588..26ab09806dad 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -139,6 +139,7 @@ struct Section {
NoBits,
Note,
Hash,
+ GnuHash,
Verdef,
Verneed,
StackSizes,
@@ -249,6 +250,39 @@ struct HashSection : Section {
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<llvm::yaml::Hex32> 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<llvm::yaml::Hex32> MaskWords;
+
+ // A shift constant used by the Bloom filter.
+ llvm::yaml::Hex32 Shift2;
+};
+
+struct GnuHashSection : Section {
+ Optional<yaml::BinaryRef> Content;
+
+ Optional<GnuHashHeader> Header;
+ Optional<std::vector<llvm::yaml::Hex64>> BloomFilter;
+ Optional<std::vector<llvm::yaml::Hex32>> HashBuckets;
+ Optional<std::vector<llvm::yaml::Hex32>> 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 @@ template <> struct MappingTraits<ELFYAML::StackSizeEntry> {
static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel);
};
+template <> struct MappingTraits<ELFYAML::GnuHashHeader> {
+ static void mapping(IO &IO, ELFYAML::GnuHashHeader &Rel);
+};
+
template <> struct MappingTraits<ELFYAML::DynamicEntry> {
static void mapping(IO &IO, ELFYAML::DynamicEntry &Rel);
};
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index 64b5cd4df5bf..be733dca6969 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -182,6 +182,9 @@ template <class ELFT> class ELFState {
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 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::NoteSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA);
+ } else if (auto S = dyn_cast<ELFYAML::GnuHashSection>(Sec)) {
+ writeSectionContent(SHeader, *S, CBA);
} else {
llvm_unreachable("Unknown section type");
}
@@ -1091,6 +1096,70 @@ void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
SHeader.sh_size = OS.tell() - Offset;
}
+template <class ELFT>
+void ELFState<ELFT>::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<uint32_t>(OS, *Section.Header->NBuckets,
+ ELFT::TargetEndianness);
+ else
+ support::endian::write<uint32_t>(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<uint32_t>(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<uint32_t>(OS, *Section.Header->MaskWords,
+ ELFT::TargetEndianness);
+ else
+ support::endian::write<uint32_t>(OS, Section.BloomFilter->size(),
+ ELFT::TargetEndianness);
+
+ // Write the shift constant used by the Bloom filter.
+ support::endian::write<uint32_t>(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<typename ELFT::uint>(OS, Val,
+ ELFT::TargetEndianness);
+
+ // Write an array of hash buckets.
+ for (llvm::yaml::Hex32 Val : *Section.HashBuckets)
+ support::endian::write<uint32_t>(OS, Val, ELFT::TargetEndianness);
+
+ // Write an array of hash values.
+ for (llvm::yaml::Hex32 Val : *Section.HashValues)
+ support::endian::write<uint32_t>(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 <class ELFT> void ELFState<ELFT>::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
index f362b9c23a7b..5872cbbe5150 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -1039,6 +1039,15 @@ static void sectionMapping(IO &IO, ELFYAML::NoteSection &Section) {
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 @@ void MappingTraits<std::unique_ptr<ELFYAML::Section>>::mapping(
Section.reset(new ELFYAML::NoteSection());
sectionMapping(IO, *cast<ELFYAML::NoteSection>(Section.get()));
break;
+ case ELF::SHT_GNU_HASH:
+ if (!IO.outputting())
+ Section.reset(new ELFYAML::GnuHashSection());
+ sectionMapping(IO, *cast<ELFYAML::GnuHashSection>(Section.get()));
+ break;
case ELF::SHT_MIPS_ABIFLAGS:
if (!IO.outputting())
Section.reset(new ELFYAML::MipsABIFlags());
@@ -1300,6 +1314,29 @@ StringRef MappingTraits<std::unique_ptr<ELFYAML::Section>>::validate(
return {};
}
+ if (const auto *Sec = dyn_cast<ELFYAML::GnuHashSection>(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 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
IO.mapRequired("Size", E.Size);
}
+void MappingTraits<ELFYAML::GnuHashHeader>::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<ELFYAML::DynamicEntry>::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
index fe1f0d3ce26b..8718f5894c68 100644
--- a/llvm/test/tools/llvm-readobj/elf-section-types.test
+++ b/llvm/test/tools/llvm-readobj/elf-section-types.test
@@ -210,6 +210,7 @@ Sections:
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
index 000000000000..24f087b68812
--- /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
index 000000000000..fff9a341910c
--- /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
index 496822ad0502..495de166723d 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -69,6 +69,7 @@ class ELFDumper {
Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::NoteSection *> dumpNoteSection(const Elf_Shdr *Shdr);
+ Expected<ELFYAML::GnuHashSection *> dumpGnuHashSection(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);
@@ -279,6 +280,13 @@ template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
Y->Sections.emplace_back(*SecOrErr);
break;
}
+ case ELF::SHT_GNU_HASH: {
+ Expected<ELFYAML::GnuHashSection *> SecOrErr = dumpGnuHashSection(&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)
@@ -762,6 +770,57 @@ ELFDumper<ELFT>::dumpHashSection(const Elf_Shdr *Shdr) {
llvm_unreachable("entries were not read correctly");
}
+template <class ELFT>
+Expected<ELFYAML::GnuHashSection *>
+ELFDumper<ELFT>::dumpGnuHashSection(const Elf_Shdr *Shdr) {
+ auto S = std::make_unique<ELFYAML::GnuHashSection>();
+ 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<uint8_t> 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 <class ELFT>
Expected<ELFYAML::VerdefSection *>
ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) {
More information about the llvm-commits
mailing list