1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[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
This commit is contained in:
georgerim 2019-10-24 15:38:53 +03:00 committed by Georgii Rymar
parent 1d76e9c5c3
commit 64b44c3d31
7 changed files with 633 additions and 0 deletions

View File

@ -139,6 +139,7 @@ struct Section {
NoBits, NoBits,
Note, Note,
Hash, Hash,
GnuHash,
Verdef, Verdef,
Verneed, Verneed,
StackSizes, StackSizes,
@ -249,6 +250,39 @@ struct HashSection : Section {
static bool classof(const Section *S) { return S->Kind == SectionKind::Hash; } 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 { struct VernauxEntry {
uint32_t Hash; uint32_t Hash;
uint16_t Flags; uint16_t Flags;
@ -541,6 +575,10 @@ template <> struct MappingTraits<ELFYAML::StackSizeEntry> {
static void mapping(IO &IO, ELFYAML::StackSizeEntry &Rel); 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> { template <> struct MappingTraits<ELFYAML::DynamicEntry> {
static void mapping(IO &IO, ELFYAML::DynamicEntry &Rel); static void mapping(IO &IO, ELFYAML::DynamicEntry &Rel);
}; };

View File

@ -182,6 +182,9 @@ template <class ELFT> class ELFState {
void writeSectionContent(Elf_Shdr &SHeader, void writeSectionContent(Elf_Shdr &SHeader,
const ELFYAML::NoteSection &Section, const ELFYAML::NoteSection &Section,
ContiguousBlobAccumulator &CBA); ContiguousBlobAccumulator &CBA);
void writeSectionContent(Elf_Shdr &SHeader,
const ELFYAML::GnuHashSection &Section,
ContiguousBlobAccumulator &CBA);
ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH); ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH);
@ -440,6 +443,8 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
writeSectionContent(SHeader, *S, CBA); writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::NoteSection>(Sec)) { } else if (auto S = dyn_cast<ELFYAML::NoteSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA); writeSectionContent(SHeader, *S, CBA);
} else if (auto S = dyn_cast<ELFYAML::GnuHashSection>(Sec)) {
writeSectionContent(SHeader, *S, CBA);
} else { } else {
llvm_unreachable("Unknown section type"); llvm_unreachable("Unknown section type");
} }
@ -1091,6 +1096,70 @@ void ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader,
SHeader.sh_size = OS.tell() - Offset; 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() { template <class ELFT> void ELFState<ELFT>::buildSectionIndex() {
for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) { for (unsigned I = 0, E = Doc.Sections.size(); I != E; ++I) {
StringRef Name = Doc.Sections[I]->Name; StringRef Name = Doc.Sections[I]->Name;

View File

@ -1039,6 +1039,15 @@ static void sectionMapping(IO &IO, ELFYAML::NoteSection &Section) {
IO.mapOptional("Notes", Section.Notes); 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) { static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) {
commonSectionMapping(IO, Section); commonSectionMapping(IO, Section);
IO.mapOptional("Size", Section.Size, Hex64(0)); IO.mapOptional("Size", Section.Size, Hex64(0));
@ -1155,6 +1164,11 @@ void MappingTraits<std::unique_ptr<ELFYAML::Section>>::mapping(
Section.reset(new ELFYAML::NoteSection()); Section.reset(new ELFYAML::NoteSection());
sectionMapping(IO, *cast<ELFYAML::NoteSection>(Section.get())); sectionMapping(IO, *cast<ELFYAML::NoteSection>(Section.get()));
break; 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: case ELF::SHT_MIPS_ABIFLAGS:
if (!IO.outputting()) if (!IO.outputting())
Section.reset(new ELFYAML::MipsABIFlags()); Section.reset(new ELFYAML::MipsABIFlags());
@ -1300,6 +1314,29 @@ StringRef MappingTraits<std::unique_ptr<ELFYAML::Section>>::validate(
return {}; 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 {}; return {};
} }
@ -1335,6 +1372,15 @@ void MappingTraits<ELFYAML::StackSizeEntry>::mapping(
IO.mapRequired("Size", E.Size); 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, void MappingTraits<ELFYAML::DynamicEntry>::mapping(IO &IO,
ELFYAML::DynamicEntry &Rel) { ELFYAML::DynamicEntry &Rel) {
assert(IO.getContext() && "The IO context is not initialized"); assert(IO.getContext() && "The IO context is not initialized");

View File

@ -210,6 +210,7 @@ Sections:
Type: SHT_GNU_ATTRIBUTES Type: SHT_GNU_ATTRIBUTES
- Name: gnu_hash - Name: gnu_hash
Type: SHT_GNU_HASH Type: SHT_GNU_HASH
Content: ""
- Name: gnu_verdef - Name: gnu_verdef
Type: SHT_GNU_verdef Type: SHT_GNU_verdef
Info: 0 Info: 0

View File

@ -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"

View File

@ -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: []

View File

@ -69,6 +69,7 @@ class ELFDumper {
Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr); Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr); Expected<ELFYAML::HashSection *> dumpHashSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::NoteSection *> dumpNoteSection(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::VerdefSection *> dumpVerdefSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::SymverSection *> dumpSymverSection(const Elf_Shdr *Shdr); Expected<ELFYAML::SymverSection *> dumpSymverSection(const Elf_Shdr *Shdr);
Expected<ELFYAML::VerneedSection *> dumpVerneedSection(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); Y->Sections.emplace_back(*SecOrErr);
break; 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: { case ELF::SHT_GNU_verdef: {
Expected<ELFYAML::VerdefSection *> SecOrErr = dumpVerdefSection(&Sec); Expected<ELFYAML::VerdefSection *> SecOrErr = dumpVerdefSection(&Sec);
if (!SecOrErr) if (!SecOrErr)
@ -762,6 +770,57 @@ ELFDumper<ELFT>::dumpHashSection(const Elf_Shdr *Shdr) {
llvm_unreachable("entries were not read correctly"); 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> template <class ELFT>
Expected<ELFYAML::VerdefSection *> Expected<ELFYAML::VerdefSection *>
ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) { ELFDumper<ELFT>::dumpVerdefSection(const Elf_Shdr *Shdr) {