[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