Index: include/llvm/ObjectYAML/ELFYAML.h =================================================================== --- include/llvm/ObjectYAML/ELFYAML.h +++ include/llvm/ObjectYAML/ELFYAML.h @@ -131,6 +131,7 @@ RawContent, Relocation, NoBits, + Hash, Verdef, Verneed, StackSizes, @@ -220,6 +221,16 @@ } }; +struct HashSection : Section { + Optional Content; + Optional> Bucket; + Optional> Chain; + + HashSection() : Section(SectionKind::Hash) {} + + static bool classof(const Section *S) { return S->Kind == SectionKind::Hash; } +}; + struct VernauxEntry { uint32_t Hash; uint16_t Flags; Index: lib/ObjectYAML/ELFEmitter.cpp =================================================================== --- lib/ObjectYAML/ELFEmitter.cpp +++ lib/ObjectYAML/ELFEmitter.cpp @@ -171,6 +171,9 @@ void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::HashSection &Section, + ContiguousBlobAccumulator &CBA); ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH); public: @@ -417,7 +420,9 @@ } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { - writeSectionContent(SHeader, *S, CBA); + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); } else { llvm_unreachable("Unknown section type"); } @@ -808,6 +813,34 @@ } } +template +void ELFState::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::HashSection &Section, + ContiguousBlobAccumulator &CBA) { + raw_ostream &OS = + CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + + unsigned Link = 0; + if (SN2I.lookup(".dynsym", Link)) + SHeader.sh_link = Link; + + if (Section.Content) { + SHeader.sh_size = writeContent(OS, Section.Content, None); + return; + } + + support::endian::write(OS, Section.Bucket->size(), + ELFT::TargetEndianness); + support::endian::write(OS, Section.Chain->size(), + ELFT::TargetEndianness); + for (uint32_t Val : *Section.Bucket) + support::endian::write(OS, Val, ELFT::TargetEndianness); + for (uint32_t Val : *Section.Chain) + support::endian::write(OS, Val, ELFT::TargetEndianness); + + SHeader.sh_size = (2 + Section.Bucket->size() + Section.Chain->size()) * 4; +} + template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::VerdefSection &Section, Index: lib/ObjectYAML/ELFYAML.cpp =================================================================== --- lib/ObjectYAML/ELFYAML.cpp +++ lib/ObjectYAML/ELFYAML.cpp @@ -1024,6 +1024,13 @@ IO.mapOptional("Entries", Section.Entries); } +static void sectionMapping(IO &IO, ELFYAML::HashSection &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Content", Section.Content); + IO.mapOptional("Bucket", Section.Bucket); + IO.mapOptional("Chain", Section.Chain); +} + static void sectionMapping(IO &IO, ELFYAML::NoBitsSection &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Size", Section.Size, Hex64(0)); @@ -1123,6 +1130,11 @@ Section.reset(new ELFYAML::NoBitsSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_HASH: + if (!IO.outputting()) + Section.reset(new ELFYAML::HashSection()); + sectionMapping(IO, *cast(Section.get())); + break; case ELF::SHT_MIPS_ABIFLAGS: if (!IO.outputting()) Section.reset(new ELFYAML::MipsABIFlags()); @@ -1196,6 +1208,24 @@ return ".stack_sizes: Content and Entries cannot be used together"; return {}; } + + if (const auto *HS = dyn_cast(Section.get())) { + if (!HS->Content && !HS->Bucket && !HS->Chain) + return "one of Content, Bucket and Chain must be specified"; + + if (HS->Content) { + if (HS->Bucket) + return "Content and Bucket cannot be used together"; + if (HS->Chain) + return "Content and Chain cannot be used together"; + return {}; + } + + if ((HS->Bucket && !HS->Chain) || (!HS->Bucket && HS->Chain)) + return "Bucket and Chain must be used together"; + return {}; + } + return {}; } Index: test/tools/llvm-readobj/elf-section-types.test =================================================================== --- test/tools/llvm-readobj/elf-section-types.test +++ test/tools/llvm-readobj/elf-section-types.test @@ -150,6 +150,7 @@ Info: progbits - Name: hash Type: SHT_HASH + Content: '' - Name: dynamic Type: SHT_DYNAMIC - Name: note Index: test/tools/llvm-size/elf-sysv.test =================================================================== --- test/tools/llvm-size/elf-sysv.test +++ test/tools/llvm-size/elf-sysv.test @@ -63,6 +63,7 @@ Address: 0x20000 - Name: .hash Type: SHT_HASH + Content: '' ShSize: 0x40 Address: 0x10000 - Name: .dynamic Index: test/tools/obj2yaml/elf-hash-section.yaml =================================================================== --- /dev/null +++ test/tools/obj2yaml/elf-hash-section.yaml @@ -0,0 +1,78 @@ +## Check how obj2yaml produces SHT_HASH section descriptions. + +## Check that obj2yaml uses "Bucket" and "Chain" tags to describe +## a SHT_HASH section when it has content of a correct size. +## I.e. data size == 4 * (2 + nbucket + nchain). + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: obj2yaml %t1 | FileCheck %s --check-prefix=CHAIN-BUCKET + +# CHAIN-BUCKET: - Name: .hash1 +# CHAIN-BUCKET-NEXT: Type: SHT_HASH +# CHAIN-BUCKET-NEXT: Bucket: [ 3 ] +# CHAIN-BUCKET-NEXT: Chain: [ 4, 5 ] +# CHAIN-BUCKET: - Name: .hash2 +# CHAIN-BUCKET-NEXT: Type: SHT_HASH +# CHAIN-BUCKET-NEXT: Bucket: [ ] +# CHAIN-BUCKET-NEXT: Chain: [ ] + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: +## Case 1: A regular case sample: nbucket == 1, nchain == 2. + - Name: .hash1 + Type: SHT_HASH + Content: '0100000002000000030000000400000005000000' +## Case 2: An empty hash table: nbucket == 0, nchain == 0. + - Name: .hash2 + Type: SHT_HASH + Content: '0000000000000000' + +## Check that obj2yaml fallbacks to using "Content" tag when +## hash sections are broken. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: obj2yaml %t2 | FileCheck %s --check-prefix=CONTENT + +# CONTENT: - Name: .empty_hash +# CONTENT-NEXT: Type: SHT_HASH +# CONTENT-NEXT: Content: '' +# CONTENT-NEXT: - Name: .invalid_header +# CONTENT-NEXT: Type: SHT_HASH +# CONTENT-NEXT: Content: '00' +# CONTENT-NEXT: - Name: .truncated +# CONTENT-NEXT: Type: SHT_HASH +# CONTENT-NEXT: Content: '01000000020000000300000004000000' +# CONTENT-NEXT: - Name: .oversized +# CONTENT-NEXT: Type: SHT_HASH +# CONTENT-NEXT: Content: '0100000002000000030000000400000000' + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: +## Case 1: section has no data. + - Name: .empty_hash + Type: SHT_HASH + Content: '' +## Case 2: section size is less than 2 * 4. + - Name: .invalid_header + Type: SHT_HASH + Content: '00' +## Case 3: nbucket == 1, nchain == 2. +## Section size is less than (2 * nbucket + nchain) * 4. + - Name: .truncated + Type: SHT_HASH + Content: '01000000020000000300000004000000' +## Case 4: nbucket == 1, nchain == 2. +## Section size is grater than (2 * nbucket + nchain) * 4. + - Name: .oversized + Type: SHT_HASH + Content: '0100000002000000030000000400000000' Index: test/tools/yaml2obj/elf-hash-section.yaml =================================================================== --- /dev/null +++ test/tools/yaml2obj/elf-hash-section.yaml @@ -0,0 +1,129 @@ +## Check how yaml2obj produces SHT_HASH sections. + +## Check we can desctibe SHT_HASH using "Content" tag. + +# RUN: yaml2obj --docnum=1 %s -o %t1 +# RUN: llvm-readobj --sections --section-data %t1 | FileCheck %s --check-prefix=CONTENT + +# CONTENT: Name: .hash +# CONTENT-NEXT: Type: SHT_HASH +# CONTENT-NEXT: Flags [ +# CONTENT-NEXT: ] +# CONTENT-NEXT: Address: 0x0 +# CONTENT-NEXT: Offset: 0x44 +# CONTENT-NEXT: Size: 20 +# CONTENT-NEXT: Link: 1 +# CONTENT-NEXT: Info: 0 +# CONTENT-NEXT: AddressAlignment: 0 +# CONTENT-NEXT: EntrySize: 0 +# CONTENT-NEXT: SectionData ( +# CONTENT-NEXT: 0000: 01000000 02000000 03000000 04000000 +# CONTENT-NEXT: 0010: 05000000 +# CONTENT-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: +## SHT_HASH is linked to dynamic symbol table by default. + - Name: .dynsym + Type: SHT_DYNSYM + - Name: .hash + Type: SHT_HASH + Content: '0100000002000000030000000400000005000000' + +## Check we can desctibe SHT_HASH using "Bucket" and "Chain" tags. + +# RUN: yaml2obj --docnum=2 %s -o %t2 +# RUN: llvm-readobj --sections --section-data %t2 | FileCheck %s --check-prefix=BUCKET-CHAIN + +# BUCKET-CHAIN: Name: .hash +# BUCKET-CHAIN: Size: +# BUCKET-CHAIN-SAME: 28 +# BUCKET-CHAIN: Link: +# BUCKET-CHAIN-SAME: 0 +# BUCKET-CHAIN: SectionData ( +# BUCKET-CHAIN-NEXT: 0000: 02000000 03000000 01000000 02000000 | +# BUCKET-CHAIN-NEXT: 0010: 03000000 04000000 05000000 | +# BUCKET-CHAIN-NEXT: ) + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Bucket: [ 1, 2 ] + Chain: [ 3, 4, 5 ] + +## Check we can't use "Content" and "Bucket" tags together. + +# RUN: not yaml2obj --docnum=3 %s 2>&1 | FileCheck %s --check-prefix=CONTENT-BUCKET + +# CONTENT-BUCKET: error: Content and Bucket cannot be used together + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Bucket: [ 1 ] + Content: '00' + +## Check we can't use "Bucket" without "Chain". + +# RUN: not yaml2obj --docnum=4 %s 2>&1 | FileCheck %s --check-prefix=NO-BUCKET-OR-CHAIN + +# NO-BUCKET-OR-CHAIN: error: Bucket and Chain must be used together + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Bucket: [ 1 ] + +## Check we can't use "Chain" without "Bucket". + +# RUN: not yaml2obj --docnum=5 %s 2>&1 | FileCheck %s --check-prefix=NO-BUCKET-OR-CHAIN + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Chain: [ 1 ] + +## Check we report an error if neither "Bucket", "Chain" or "Content" were set. + +# RUN: not yaml2obj --docnum=6 %s 2>&1 | FileCheck %s --check-prefix=NO-TAGS + +# NO-TAGS: error: one of Content, Bucket and Chain must be specified + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH Index: tools/obj2yaml/elf2yaml.cpp =================================================================== --- tools/obj2yaml/elf2yaml.cpp +++ tools/obj2yaml/elf2yaml.cpp @@ -63,6 +63,7 @@ Expected dumpSymtabShndxSection(const Elf_Shdr *Shdr); Expected dumpNoBitsSection(const Elf_Shdr *Shdr); + Expected dumpHashSection(const Elf_Shdr *Shdr); Expected dumpVerdefSection(const Elf_Shdr *Shdr); Expected dumpSymverSection(const Elf_Shdr *Shdr); Expected dumpVerneedSection(const Elf_Shdr *Shdr); @@ -255,6 +256,13 @@ Y->Sections.emplace_back(*SecOrErr); break; } + case ELF::SHT_HASH: { + Expected SecOrErr = dumpHashSection(&Sec); + if (!SecOrErr) + return SecOrErr.takeError(); + Y->Sections.emplace_back(*SecOrErr); + break; + } case ELF::SHT_GNU_verdef: { Expected SecOrErr = dumpVerdefSection(&Sec); if (!SecOrErr) @@ -616,6 +624,46 @@ return S.release(); } +template +Expected +ELFDumper::dumpHashSection(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(); + + ArrayRef Content = *ContentOrErr; + if (Content.size() % 4 != 0 || Content.size() < 8) { + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + DataExtractor::Cursor Cur(0); + DataExtractor Data(Content, Obj.isLE(), ELFT::Is64Bits ? 8 : 4); + uint32_t NBucket = Data.getUnsigned(Cur, 4); + uint32_t NChain = Data.getUnsigned(Cur, 4); + if (Content.size() != (2 + NBucket + NChain) * 4) { + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + std::vector Bucket; + while (NBucket--) + Bucket.push_back(Data.getUnsigned(Cur, 4)); + S->Bucket = std::move(Bucket); + + std::vector Chain; + while (NChain--) + Chain.push_back(Data.getUnsigned(Cur, 4)); + S->Chain = std::move(Chain); + + assert(Cur && "entries were not read correctly"); + return S.release(); +} + template Expected ELFDumper::dumpVerdefSection(const Elf_Shdr *Shdr) {