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:
parent
1d76e9c5c3
commit
64b44c3d31
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
|
@ -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
|
||||||
|
132
test/tools/obj2yaml/elf-gnu-hash-section.yaml
Normal file
132
test/tools/obj2yaml/elf-gnu-hash-section.yaml
Normal 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"
|
288
test/tools/yaml2obj/elf-gnu-hash-section.yaml
Normal file
288
test/tools/yaml2obj/elf-gnu-hash-section.yaml
Normal 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: []
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user