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 non-empty hash table: 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 falls back to using the "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 greater 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,147 @@ +## Check how yaml2obj produces SHT_HASH sections. + +## Check we can describe a SHT_HASH section using the "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 describe a SHT_HASH section 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 "Content" and "Chain" tags together. + +# RUN: not yaml2obj --docnum=4 %s 2>&1 | FileCheck %s --check-prefix=CONTENT-CHAIN + +# CONTENT-CHAIN: error: "Content" and "Chain" cannot be used together + +--- !ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_386 +Sections: + - Name: .hash + Type: SHT_HASH + Chain: [ 1 ] + Content: '00' + +## Check we can't use "Bucket" without "Chain". + +# RUN: not yaml2obj --docnum=5 %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=6 %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" nor "Content" were set. + +# RUN: not yaml2obj --docnum=7 %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,45 @@ 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(), /*AddressSize=*/0); + uint32_t NBucket = Data.getU32(Cur); + uint32_t NChain = Data.getU32(Cur); + if (Content.size() != (2 + NBucket + NChain) * 4) { + S->Content = yaml::BinaryRef(Content); + return S.release(); + } + + S->Bucket.emplace(NBucket); + for (uint32_t &V : *S->Bucket) + V = Data.getU32(Cur); + + S->Chain.emplace(NChain); + for (uint32_t &V : *S->Chain) + V = Data.getU32(Cur); + + if (!Cur) + llvm_unreachable("entries were not read correctly"); + return S.release(); +} + template Expected ELFDumper::dumpVerdefSection(const Elf_Shdr *Shdr) {