[llvm-branch-commits] [llvm] 9c89dcf - [yaml2obj, obj2yaml] - Implement section header table as a special Chunk.

Georgii Rymar via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Jan 25 02:13:09 PST 2021


Author: Georgii Rymar
Date: 2021-01-25T13:08:08+03:00
New Revision: 9c89dcf80736a7c0710dc4c237ec35f0687e1efd

URL: https://github.com/llvm/llvm-project/commit/9c89dcf80736a7c0710dc4c237ec35f0687e1efd
DIFF: https://github.com/llvm/llvm-project/commit/9c89dcf80736a7c0710dc4c237ec35f0687e1efd.diff

LOG: [yaml2obj, obj2yaml] - Implement section header table as a special Chunk.

This was discussed in D93678 thread.
Currently we have one special chunk - Fill.

This patch re implements the "SectionHeaderTable" key to become a special chunk too.
With that we are able to place the section header table at any location,
just like we place sections.

Differential revision: https://reviews.llvm.org/D95140

Added: 
    

Modified: 
    llvm/include/llvm/ObjectYAML/ELFYAML.h
    llvm/lib/ObjectYAML/ELFEmitter.cpp
    llvm/lib/ObjectYAML/ELFYAML.cpp
    llvm/test/Object/obj2yaml.test
    llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test
    llvm/test/tools/llvm-readobj/ELF/dynamic-reloc-no-section-headers.test
    llvm/test/tools/llvm-readobj/ELF/file-headers.test
    llvm/test/tools/llvm-readobj/ELF/hash-table.test
    llvm/test/tools/llvm-readobj/ELF/malformed-pt-dynamic.test
    llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test
    llvm/test/tools/obj2yaml/ELF/offset.yaml
    llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml
    llvm/test/tools/yaml2obj/ELF/section-headers.yaml
    llvm/test/tools/yaml2obj/ELF/verdef-section.yaml
    llvm/test/tools/yaml2obj/ELF/verneed-section.yaml
    llvm/test/tools/yaml2obj/ELF/versym-section.yaml
    llvm/tools/obj2yaml/elf2yaml.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h
index 98ba8cda372b..cae3b435f3b0 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -128,12 +128,6 @@ struct SectionHeader {
   StringRef Name;
 };
 
-struct SectionHeaderTable {
-  Optional<std::vector<SectionHeader>> Sections;
-  Optional<std::vector<SectionHeader>> Excluded;
-  Optional<bool> NoHeaders;
-};
-
 struct Symbol {
   StringRef Name;
   ELF_STT Type;
@@ -196,18 +190,26 @@ struct Chunk {
     ARMIndexTable,
     MipsABIFlags,
     Addrsig,
-    Fill,
     LinkerOptions,
     DependentLibraries,
     CallGraphProfile,
-    BBAddrMap
+    BBAddrMap,
+
+    // Special chunks.
+    SpecialChunksStart,
+    Fill = SpecialChunksStart,
+    SectionHeaderTable,
   };
 
   ChunkKind Kind;
   StringRef Name;
   Optional<llvm::yaml::Hex64> Offset;
 
-  Chunk(ChunkKind K) : Kind(K) {}
+  // Usually chunks are not created implicitly, but rather loaded from YAML.
+  // This flag is used to signal whether this is the case or not.
+  bool IsImplicit;
+
+  Chunk(ChunkKind K, bool Implicit) : Kind(K), IsImplicit(Implicit) {}
   virtual ~Chunk();
 };
 
@@ -222,17 +224,14 @@ struct Section : public Chunk {
   Optional<yaml::BinaryRef> Content;
   Optional<llvm::yaml::Hex64> Size;
 
-  // Usually sections are not created implicitly, but loaded from YAML.
-  // When they are, this flag is used to signal about that.
-  bool IsImplicit;
-
   // Holds the original section index.
   unsigned OriginalSecNdx;
 
-  Section(ChunkKind Kind, bool IsImplicit = false)
-      : Chunk(Kind), IsImplicit(IsImplicit) {}
+  Section(ChunkKind Kind, bool IsImplicit = false) : Chunk(Kind, IsImplicit) {}
 
-  static bool classof(const Chunk *S) { return S->Kind != ChunkKind::Fill; }
+  static bool classof(const Chunk *S) {
+    return S->Kind < ChunkKind::SpecialChunksStart;
+  }
 
   // Some derived sections might have their own special entries. This method
   // returns a vector of <entry name, is used> pairs. It is used for section
@@ -276,11 +275,34 @@ struct Fill : Chunk {
   Optional<yaml::BinaryRef> Pattern;
   llvm::yaml::Hex64 Size;
 
-  Fill() : Chunk(ChunkKind::Fill) {}
+  Fill() : Chunk(ChunkKind::Fill, /*Implicit=*/false) {}
 
   static bool classof(const Chunk *S) { return S->Kind == ChunkKind::Fill; }
 };
 
+struct SectionHeaderTable : Chunk {
+  SectionHeaderTable(bool IsImplicit)
+      : Chunk(ChunkKind::SectionHeaderTable, IsImplicit) {}
+
+  static bool classof(const Chunk *S) {
+    return S->Kind == ChunkKind::SectionHeaderTable;
+  }
+
+  Optional<std::vector<SectionHeader>> Sections;
+  Optional<std::vector<SectionHeader>> Excluded;
+  Optional<bool> NoHeaders;
+
+  size_t getNumHeaders(size_t SectionsNum) const {
+    if (IsImplicit)
+      return SectionsNum;
+    if (NoHeaders)
+      return (*NoHeaders) ? 0 : SectionsNum;
+    return (Sections ? Sections->size() : 0) + /*Null section*/ 1;
+  }
+
+  static constexpr StringRef TypeStr = "SectionHeaderTable";
+};
+
 struct BBAddrMapSection : Section {
   Optional<std::vector<BBAddrMapEntry>> Entries;
 
@@ -665,7 +687,6 @@ struct ProgramHeader {
 
 struct Object {
   FileHeader Header;
-  Optional<SectionHeaderTable> SectionHeaders;
   std::vector<ProgramHeader> ProgramHeaders;
 
   // An object might contain output section descriptions as well as
@@ -688,6 +709,13 @@ struct Object {
     return Ret;
   }
 
+  const SectionHeaderTable &getSectionHeaderTable() const {
+    for (const std::unique_ptr<Chunk> &C : Chunks)
+      if (auto *S = dyn_cast<ELFYAML::SectionHeaderTable>(C.get()))
+        return *S;
+    llvm_unreachable("the section header table chunk must always be present");
+  }
+
   unsigned getMachine() const;
 };
 
@@ -837,11 +865,6 @@ struct MappingTraits<ELFYAML::FileHeader> {
   static void mapping(IO &IO, ELFYAML::FileHeader &FileHdr);
 };
 
-template <> struct MappingTraits<ELFYAML::SectionHeaderTable> {
-  static void mapping(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable);
-  static std::string validate(IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable);
-};
-
 template <> struct MappingTraits<ELFYAML::SectionHeader> {
   static void mapping(IO &IO, ELFYAML::SectionHeader &SHdr);
 };

diff  --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index 752a037d61b1..5f1b031fe1d8 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -124,6 +124,11 @@ class ContiguousBlobAccumulator {
     if (checkLimit(sizeof(T)))
       support::endian::write<T>(OS, Val, E);
   }
+
+  void updateDataAt(uint64_t Pos, void *Data, size_t Size) {
+    assert(Pos >= InitialOffset && Pos + Size <= getOffset());
+    memcpy(&Buf[Pos - InitialOffset], Data, Size);
+  }
 };
 
 // Used to keep track of section and symbol names, so that in the YAML file
@@ -224,7 +229,7 @@ template <class ELFT> class ELFState {
                    ArrayRef<typename ELFT::Shdr> SHeaders);
 
   void finalizeStrings();
-  void writeELFHeader(raw_ostream &OS, Optional<uint64_t> SHOff);
+  void writeELFHeader(raw_ostream &OS);
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::NoBitsSection &Section,
                            ContiguousBlobAccumulator &CBA);
@@ -330,12 +335,22 @@ ELFState<ELFT>::ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH)
         std::make_unique<ELFYAML::Section>(
             ELFYAML::Chunk::ChunkKind::RawContent, /*IsImplicit=*/true));
 
-  // We add a technical suffix for each unnamed section/fill. It does not affect
-  // the output, but allows us to map them by name in the code and report better
-  // error messages.
   StringSet<> DocSections;
+  ELFYAML::SectionHeaderTable *SecHdrTable = nullptr;
   for (size_t I = 0; I < Doc.Chunks.size(); ++I) {
     const std::unique_ptr<ELFYAML::Chunk> &C = Doc.Chunks[I];
+
+    // We might have an explicit section header table declaration.
+    if (auto S = dyn_cast<ELFYAML::SectionHeaderTable>(C.get())) {
+      if (SecHdrTable)
+        reportError("multiple section header tables are not allowed");
+      SecHdrTable = S;
+      continue;
+    }
+
+    // We add a technical suffix for each unnamed section/fill. It does not
+    // affect the output, but allows us to map them by name in the code and
+    // report better error messages.
     if (C->Name.empty()) {
       std::string NewName = ELFYAML::appendUniqueSuffix(
           /*Name=*/"", "index " + Twine(I));
@@ -359,7 +374,7 @@ ELFState<ELFT>::ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH)
       ImplicitSections.push_back(StringRef(SecName).copy(StringAlloc));
     }
   ImplicitSections.insert(ImplicitSections.end(), {".strtab"});
-  if (!Doc.SectionHeaders || !Doc.SectionHeaders->NoHeaders.getValueOr(false))
+  if (!SecHdrTable || !SecHdrTable->NoHeaders.getValueOr(false))
     ImplicitSections.insert(ImplicitSections.end(), {".shstrtab"});
 
   // Insert placeholders for implicit sections that are not
@@ -371,12 +386,27 @@ ELFState<ELFT>::ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH)
     std::unique_ptr<ELFYAML::Chunk> Sec = std::make_unique<ELFYAML::Section>(
         ELFYAML::Chunk::ChunkKind::RawContent, true /*IsImplicit*/);
     Sec->Name = SecName;
-    Doc.Chunks.push_back(std::move(Sec));
+
+    // When the section header table is explicitly defined at the end of the
+    // sections list, it is reasonable to assume that the user wants to reorder
+    // section headers, but still wants to place the section header table after
+    // all sections, like it normally happens. In this case we want to insert
+    // other implicit sections right before the section header table.
+    if (Doc.Chunks.back().get() == SecHdrTable)
+      Doc.Chunks.insert(Doc.Chunks.end() - 1, std::move(Sec));
+    else
+      Doc.Chunks.push_back(std::move(Sec));
   }
+
+  // Insert the section header table implicitly at the end, when it is not
+  // explicitly defined.
+  if (!SecHdrTable)
+    Doc.Chunks.push_back(
+        std::make_unique<ELFYAML::SectionHeaderTable>(/*IsImplicit=*/true));
 }
 
 template <class ELFT>
-void ELFState<ELFT>::writeELFHeader(raw_ostream &OS, Optional<uint64_t> SHOff) {
+void ELFState<ELFT>::writeELFHeader(raw_ostream &OS) {
   using namespace llvm::ELF;
 
   Elf_Ehdr Header;
@@ -426,29 +456,24 @@ void ELFState<ELFT>::writeELFHeader(raw_ostream &OS, Optional<uint64_t> SHOff) {
   Header.e_shentsize = Doc.Header.EShEntSize ? (uint16_t)*Doc.Header.EShEntSize
                                              : sizeof(Elf_Shdr);
 
+  const ELFYAML::SectionHeaderTable &SectionHeaders =
+      Doc.getSectionHeaderTable();
+
   if (Doc.Header.EShOff)
     Header.e_shoff = *Doc.Header.EShOff;
-  else if (SHOff)
-    Header.e_shoff = *SHOff;
+  else if (SectionHeaders.Offset)
+    Header.e_shoff = *SectionHeaders.Offset;
   else
     Header.e_shoff = 0;
 
   if (Doc.Header.EShNum)
     Header.e_shnum = *Doc.Header.EShNum;
-  else if (!Doc.SectionHeaders ||
-           (Doc.SectionHeaders->NoHeaders && !*Doc.SectionHeaders->NoHeaders))
-    Header.e_shnum = Doc.getSections().size();
-  else if (!SHOff)
-    Header.e_shnum = 0;
   else
-    Header.e_shnum =
-        (Doc.SectionHeaders->Sections ? Doc.SectionHeaders->Sections->size()
-                                      : 0) +
-        /*Null section*/ 1;
+    Header.e_shnum = SectionHeaders.getNumHeaders(Doc.getSections().size());
 
   if (Doc.Header.EShStrNdx)
     Header.e_shstrndx = *Doc.Header.EShStrNdx;
-  else if (SHOff && !ExcludedSectionHeaders.count(".shstrtab"))
+  else if (SectionHeaders.Offset && !ExcludedSectionHeaders.count(".shstrtab"))
     Header.e_shstrndx = SN2I.get(".shstrtab");
   else
     Header.e_shstrndx = 0;
@@ -520,14 +545,16 @@ unsigned ELFState<ELFT>::toSectionIndex(StringRef S, StringRef LocSec,
     return 0;
   }
 
-  if (!Doc.SectionHeaders || (Doc.SectionHeaders->NoHeaders &&
-                              !Doc.SectionHeaders->NoHeaders.getValue()))
+  const ELFYAML::SectionHeaderTable &SectionHeaders =
+      Doc.getSectionHeaderTable();
+  if (SectionHeaders.IsImplicit ||
+      (SectionHeaders.NoHeaders && !SectionHeaders.NoHeaders.getValue()))
     return Index;
 
-  assert(!Doc.SectionHeaders->NoHeaders.getValueOr(false) ||
-         !Doc.SectionHeaders->Sections);
+  assert(!SectionHeaders.NoHeaders.getValueOr(false) ||
+         !SectionHeaders.Sections);
   size_t FirstExcluded =
-      Doc.SectionHeaders->Sections ? Doc.SectionHeaders->Sections->size() : 0;
+      SectionHeaders.Sections ? SectionHeaders.Sections->size() : 0;
   if (Index >= FirstExcluded) {
     if (LocSym.empty())
       reportError("unable to link '" + LocSec + "' to excluded section '" + S +
@@ -670,8 +697,27 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
       continue;
     }
 
+    if (ELFYAML::SectionHeaderTable *S =
+            dyn_cast<ELFYAML::SectionHeaderTable>(D.get())) {
+      if (S->NoHeaders.getValueOr(false))
+        continue;
+
+      if (!S->Offset)
+        S->Offset = alignToOffset(CBA, sizeof(typename ELFT::uint),
+                                  /*Offset=*/None);
+      else
+        S->Offset = alignToOffset(CBA, /*Align=*/1, S->Offset);
+
+      uint64_t Size = S->getNumHeaders(SHeaders.size()) * sizeof(Elf_Shdr);
+      // The full section header information might be not available here, so
+      // fill the space with zeroes as a placeholder.
+      CBA.writeZeros(Size);
+      LocationCounter += Size;
+      continue;
+    }
+
     ELFYAML::Section *Sec = cast<ELFYAML::Section>(D.get());
-    bool IsFirstUndefSection = D == Doc.Chunks.front();
+    bool IsFirstUndefSection = Sec == Doc.getSections().front();
     if (IsFirstUndefSection && Sec->IsImplicit)
       continue;
 
@@ -1742,7 +1788,9 @@ void ELFState<ELFT>::writeFill(ELFYAML::Fill &Fill,
 
 template <class ELFT>
 DenseMap<StringRef, size_t> ELFState<ELFT>::buildSectionHeaderReorderMap() {
-  if (!Doc.SectionHeaders || Doc.SectionHeaders->NoHeaders)
+  const ELFYAML::SectionHeaderTable &SectionHeaders =
+      Doc.getSectionHeaderTable();
+  if (SectionHeaders.IsImplicit || SectionHeaders.NoHeaders)
     return DenseMap<StringRef, size_t>();
 
   DenseMap<StringRef, size_t> Ret;
@@ -1756,12 +1804,12 @@ DenseMap<StringRef, size_t> ELFState<ELFT>::buildSectionHeaderReorderMap() {
     Seen.insert(Hdr.Name);
   };
 
-  if (Doc.SectionHeaders->Sections)
-    for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Sections)
+  if (SectionHeaders.Sections)
+    for (const ELFYAML::SectionHeader &Hdr : *SectionHeaders.Sections)
       AddSection(Hdr);
 
-  if (Doc.SectionHeaders->Excluded)
-    for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded)
+  if (SectionHeaders.Excluded)
+    for (const ELFYAML::SectionHeader &Hdr : *SectionHeaders.Excluded)
       AddSection(Hdr);
 
   for (const ELFYAML::Section *S : Doc.getSections()) {
@@ -1790,17 +1838,17 @@ template <class ELFT> void ELFState<ELFT>::buildSectionIndex() {
 
   // Build excluded section headers map.
   std::vector<ELFYAML::Section *> Sections = Doc.getSections();
-  if (Doc.SectionHeaders) {
-    if (Doc.SectionHeaders->Excluded)
-      for (const ELFYAML::SectionHeader &Hdr : *Doc.SectionHeaders->Excluded)
-        if (!ExcludedSectionHeaders.insert(Hdr.Name).second)
-          llvm_unreachable("buildSectionIndex() failed");
-
-    if (Doc.SectionHeaders->NoHeaders.getValueOr(false))
-      for (const ELFYAML::Section *S : Sections)
-        if (!ExcludedSectionHeaders.insert(S->Name).second)
-          llvm_unreachable("buildSectionIndex() failed");
-  }
+  const ELFYAML::SectionHeaderTable &SectionHeaders =
+      Doc.getSectionHeaderTable();
+  if (SectionHeaders.Excluded)
+    for (const ELFYAML::SectionHeader &Hdr : *SectionHeaders.Excluded)
+      if (!ExcludedSectionHeaders.insert(Hdr.Name).second)
+        llvm_unreachable("buildSectionIndex() failed");
+
+  if (SectionHeaders.NoHeaders.getValueOr(false))
+    for (const ELFYAML::Section *S : Sections)
+      if (!ExcludedSectionHeaders.insert(S->Name).second)
+        llvm_unreachable("buildSectionIndex() failed");
 
   size_t SecNdx = -1;
   for (const ELFYAML::Section *S : Sections) {
@@ -1903,17 +1951,7 @@ bool ELFState<ELFT>::writeELF(raw_ostream &OS, ELFYAML::Object &Doc,
   // Now we can decide segment offsets.
   State.setProgramHeaderLayout(PHeaders, SHeaders);
 
-  // If needed, align the start of the section header table, which is written
-  // after all section data.
-  const bool HasSectionHeaders =
-      !Doc.SectionHeaders || !Doc.SectionHeaders->NoHeaders.getValueOr(false);
-  Optional<uint64_t> SHOff;
-  if (HasSectionHeaders)
-    SHOff = State.alignToOffset(CBA, sizeof(typename ELFT::uint),
-                                /*Offset=*/None);
-  bool ReachedLimit = SHOff.getValueOr(CBA.getOffset()) +
-                          arrayDataSize(makeArrayRef(SHeaders)) >
-                      MaxSize;
+  bool ReachedLimit = CBA.getOffset() > MaxSize;
   if (Error E = CBA.takeLimitError()) {
     // We report a custom error message instead below.
     consumeError(std::move(E));
@@ -1928,11 +1966,15 @@ bool ELFState<ELFT>::writeELF(raw_ostream &OS, ELFYAML::Object &Doc,
   if (State.HasError)
     return false;
 
-  State.writeELFHeader(OS, SHOff);
+  State.writeELFHeader(OS);
   writeArrayData(OS, makeArrayRef(PHeaders));
+
+  const ELFYAML::SectionHeaderTable &SHT = Doc.getSectionHeaderTable();
+  if (!SHT.NoHeaders.getValueOr(false))
+    CBA.updateDataAt(*SHT.Offset, SHeaders.data(),
+                     SHT.getNumHeaders(SHeaders.size()) * sizeof(Elf_Shdr));
+
   CBA.writeBlobToStream(OS);
-  if (HasSectionHeaders)
-    writeArrayData(OS, makeArrayRef(SHeaders));
   return true;
 }
 

diff  --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 84022cf6b416..b81d9a81758a 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -860,23 +860,6 @@ void MappingTraits<ELFYAML::SectionHeader>::mapping(
   IO.mapRequired("Name", SHdr.Name);
 }
 
-void MappingTraits<ELFYAML::SectionHeaderTable>::mapping(
-    IO &IO, ELFYAML::SectionHeaderTable &SectionHeader) {
-  IO.mapOptional("Sections", SectionHeader.Sections);
-  IO.mapOptional("Excluded", SectionHeader.Excluded);
-  IO.mapOptional("NoHeaders", SectionHeader.NoHeaders);
-}
-
-std::string MappingTraits<ELFYAML::SectionHeaderTable>::validate(
-    IO &IO, ELFYAML::SectionHeaderTable &SecHdrTable) {
-  if (SecHdrTable.NoHeaders && (SecHdrTable.Sections || SecHdrTable.Excluded))
-    return "NoHeaders can't be used together with Sections/Excluded";
-  if (!SecHdrTable.NoHeaders && !SecHdrTable.Sections && !SecHdrTable.Excluded)
-    return "SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop the "
-           "section header table";
-  return "";
-}
-
 void MappingTraits<ELFYAML::FileHeader>::mapping(IO &IO,
                                                  ELFYAML::FileHeader &FileHdr) {
   IO.mapRequired("Class", FileHdr.Class);
@@ -1252,6 +1235,14 @@ static void fillMapping(IO &IO, ELFYAML::Fill &Fill) {
   IO.mapRequired("Size", Fill.Size);
 }
 
+static void sectionHeaderTableMapping(IO &IO,
+                                      ELFYAML::SectionHeaderTable &SHT) {
+  IO.mapOptional("Offset", SHT.Offset);
+  IO.mapOptional("Sections", SHT.Sections);
+  IO.mapOptional("Excluded", SHT.Excluded);
+  IO.mapOptional("NoHeaders", SHT.NoHeaders);
+}
+
 static void sectionMapping(IO &IO, ELFYAML::LinkerOptionsSection &Section) {
   commonSectionMapping(IO, Section);
   IO.mapOptional("Options", Section.Options);
@@ -1304,22 +1295,48 @@ static StringRef getStringValue(IO &IO, const char *Key) {
   return Val;
 }
 
+static void setStringValue(IO &IO, const char *Key, StringRef Val) {
+  IO.mapRequired(Key, Val);
+}
+
+static bool isInteger(StringRef Val) {
+  APInt Tmp;
+  return !Val.getAsInteger(0, Tmp);
+}
+
 void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
     IO &IO, std::unique_ptr<ELFYAML::Chunk> &Section) {
   ELFYAML::ELF_SHT Type;
+  StringRef TypeStr;
   if (IO.outputting()) {
-    Type = cast<ELFYAML::Section>(Section.get())->Type;
+    if (auto *S = dyn_cast<ELFYAML::Section>(Section.get()))
+      Type = S->Type;
+    else if (auto *SHT = dyn_cast<ELFYAML::SectionHeaderTable>(Section.get()))
+      TypeStr = SHT->TypeStr;
   } else {
     // When the Type string does not have a "SHT_" prefix, we know it is not a
-    // description of a regular ELF output section. Currently, we have one
-    // special type named "Fill". See comments for Fill.
-    if (getStringValue(IO, "Type") == "Fill") {
-      Section.reset(new ELFYAML::Fill());
-      fillMapping(IO, *cast<ELFYAML::Fill>(Section.get()));
-      return;
-    }
+    // description of a regular ELF output section.
+    TypeStr = getStringValue(IO, "Type");
+    if (TypeStr.startswith("SHT_") || isInteger(TypeStr))
+      IO.mapRequired("Type", Type);
+  }
+
+  if (TypeStr == "Fill") {
+    assert(!IO.outputting()); // We don't dump fills currently.
+    Section.reset(new ELFYAML::Fill());
+    fillMapping(IO, *cast<ELFYAML::Fill>(Section.get()));
+    return;
+  }
+
+  if (TypeStr == ELFYAML::SectionHeaderTable::TypeStr) {
+    if (IO.outputting())
+      setStringValue(IO, "Type", TypeStr);
+    else
+      Section.reset(new ELFYAML::SectionHeaderTable(/*IsImplicit=*/false));
 
-    IO.mapRequired("Type", Type);
+    sectionHeaderTableMapping(
+        IO, *cast<ELFYAML::SectionHeaderTable>(Section.get()));
+    return;
   }
 
   const auto &Obj = *static_cast<ELFYAML::Object *>(IO.getContext());
@@ -1452,6 +1469,15 @@ std::string MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::validate(
     return "";
   }
 
+  if (const auto *SHT = dyn_cast<ELFYAML::SectionHeaderTable>(C.get())) {
+    if (SHT->NoHeaders && (SHT->Sections || SHT->Excluded || SHT->Offset))
+      return "NoHeaders can't be used together with Offset/Sections/Excluded";
+    if (!SHT->NoHeaders && !SHT->Sections && !SHT->Excluded)
+      return "SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop "
+             "the section header table";
+    return "";
+  }
+
   const ELFYAML::Section &Sec = *cast<ELFYAML::Section>(C.get());
   if (Sec.Size && Sec.Content &&
       (uint64_t)(*Sec.Size) < Sec.Content->binary_size())
@@ -1656,7 +1682,6 @@ void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) {
   IO.mapOptional("Symbols", Object.Symbols);
   IO.mapOptional("DynamicSymbols", Object.DynamicSymbols);
   IO.mapOptional("DWARF", Object.DWARF);
-  IO.mapOptional("SectionHeaderTable", Object.SectionHeaders);
   if (Object.DWARF) {
     Object.DWARF->IsLittleEndian =
         Object.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB);

diff  --git a/llvm/test/Object/obj2yaml.test b/llvm/test/Object/obj2yaml.test
index f57af6f1fca1..f0f25ace3c1a 100644
--- a/llvm/test/Object/obj2yaml.test
+++ b/llvm/test/Object/obj2yaml.test
@@ -415,6 +415,19 @@
 # ELF-MIPSEL-NEXT:       - Offset:          0x2C
 # ELF-MIPSEL-NEXT:         Symbol:          SomeOtherFunction
 # ELF-MIPSEL-NEXT:         Type:            R_MIPS_CALL16
+# ELF-MIPSEL-NEXT:   - Type: SectionHeaderTable
+# ELF-MIPSEL-NEXT:     Sections:
+# ELF-MIPSEL-NEXT:       - Name: .text
+# ELF-MIPSEL-NEXT:       - Name: .rel.text
+# ELF-MIPSEL-NEXT:       - Name: .data
+# ELF-MIPSEL-NEXT:       - Name: .bss
+# ELF-MIPSEL-NEXT:       - Name: .mdebug.abi32
+# ELF-MIPSEL-NEXT:       - Name: .rodata.str1.1
+# ELF-MIPSEL-NEXT:       - Name: .reginfo
+# ELF-MIPSEL-NEXT:       - Name: .MIPS.abiflags
+# ELF-MIPSEL-NEXT:       - Name: .shstrtab
+# ELF-MIPSEL-NEXT:       - Name: .symtab
+# ELF-MIPSEL-NEXT:       - Name: .strtab
 # ELF-MIPSEL-NEXT: Symbols:
 # ELF-MIPSEL-NEXT:     - Name:            trivial.ll
 # ELF-MIPSEL-NEXT:       Type:            STT_FILE
@@ -460,19 +473,6 @@
 # ELF-MIPSEL-NEXT:       Binding:         STB_GLOBAL
 # ELF-MIPSEL-NEXT:     - Name:            puts
 # ELF-MIPSEL-NEXT:       Binding:         STB_GLOBAL
-# ELF-MIPSEL-NEXT: SectionHeaderTable:
-# ELF-MIPSEL-NEXT:   Sections:
-# ELF-MIPSEL-NEXT:     - Name: .text
-# ELF-MIPSEL-NEXT:     - Name: .rel.text
-# ELF-MIPSEL-NEXT:     - Name: .data
-# ELF-MIPSEL-NEXT:     - Name: .bss
-# ELF-MIPSEL-NEXT:     - Name: .mdebug.abi32
-# ELF-MIPSEL-NEXT:     - Name: .rodata.str1.1
-# ELF-MIPSEL-NEXT:     - Name: .reginfo
-# ELF-MIPSEL-NEXT:     - Name: .MIPS.abiflags
-# ELF-MIPSEL-NEXT:     - Name: .shstrtab
-# ELF-MIPSEL-NEXT:     - Name: .symtab
-# ELF-MIPSEL-NEXT:     - Name: .strtab
 # ELF-MIPSEL-NEXT: ...
 
 # RUN: obj2yaml %p/Inputs/trivial-object-test.elf-mips64el | FileCheck %s --check-prefix ELF-MIPS64EL
@@ -514,6 +514,17 @@
 # ELF-MIPS64EL-NEXT:     Relocations:
 # ELF-MIPS64EL-NEXT:       - Symbol:          zed
 # ELF-MIPS64EL-NEXT:         Type:            R_MIPS_64
+# ELF-MIPS64EL-NEXT:   - Type: SectionHeaderTable
+# ELF-MIPS64EL-NEXT:     Sections:
+# ELF-MIPS64EL-NEXT:       - Name: .text
+# ELF-MIPS64EL-NEXT:       - Name: .data
+# ELF-MIPS64EL-NEXT:       - Name: .rela.data
+# ELF-MIPS64EL-NEXT:       - Name: .bss
+# ELF-MIPS64EL-NEXT:       - Name: .MIPS.options
+# ELF-MIPS64EL-NEXT:       - Name: .pdr
+# ELF-MIPS64EL-NEXT:       - Name: .shstrtab
+# ELF-MIPS64EL-NEXT:       - Name: .symtab
+# ELF-MIPS64EL-NEXT:       - Name: .strtab
 # ELF-MIPS64EL-NEXT: Symbols:
 # ELF-MIPS64EL-NEXT:     - Name:            .text
 # ELF-MIPS64EL-NEXT:       Type:            STT_SECTION
@@ -534,17 +545,6 @@
 # ELF-MIPS64EL-NEXT:       Section:         .pdr
 # ELF-MIPS64EL-NEXT:     - Name:            zed
 # ELF-MIPS64EL-NEXT:       Binding:         STB_GLOBAL
-# ELF-MIPS64EL-NEXT: SectionHeaderTable:
-# ELF-MIPS64EL-NEXT:   Sections:
-# ELF-MIPS64EL-NEXT:     - Name: .text
-# ELF-MIPS64EL-NEXT:     - Name: .data
-# ELF-MIPS64EL-NEXT:     - Name: .rela.data
-# ELF-MIPS64EL-NEXT:     - Name: .bss
-# ELF-MIPS64EL-NEXT:     - Name: .MIPS.options
-# ELF-MIPS64EL-NEXT:     - Name: .pdr
-# ELF-MIPS64EL-NEXT:     - Name: .shstrtab
-# ELF-MIPS64EL-NEXT:     - Name: .symtab
-# ELF-MIPS64EL-NEXT:     - Name: .strtab
 # ELF-MIPS64EL-NEXT: ...
 
 # RUN: yaml2obj %s -o %t-x86-64

diff  --git a/llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test b/llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test
index cdf205981420..1a1c6dd4d0d1 100644
--- a/llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test
+++ b/llvm/test/tools/llvm-readobj/ELF/broken-dynamic-reloc.test
@@ -447,6 +447,8 @@ Sections:
     Flags:   [ SHF_ALLOC ]
     Address: 0x300
     Offset:  0x300
+  - Type: SectionHeaderTable
+    NoHeaders: true
 DynamicSymbols:
   - Name: foo
 ProgramHeaders:
@@ -457,5 +459,3 @@ ProgramHeaders:
   - Type:     PT_DYNAMIC
     FirstSec: .dynamic
     LastSec:  .dynamic
-SectionHeaderTable:
-  NoHeaders: true

diff  --git a/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc-no-section-headers.test b/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc-no-section-headers.test
index ae99d76b8f8b..4702af6af694 100644
--- a/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc-no-section-headers.test
+++ b/llvm/test/tools/llvm-readobj/ELF/dynamic-reloc-no-section-headers.test
@@ -50,6 +50,8 @@ Sections:
         Value: 0x0
   - Name:    .dynsym
     Type:    SHT_DYNSYM
+  - Type: SectionHeaderTable
+    NoHeaders: true
 DynamicSymbols:
   - Name: foo
 ProgramHeaders:
@@ -59,5 +61,3 @@ ProgramHeaders:
   - Type:     PT_DYNAMIC
     FirstSec: .dynamic
     LastSec:  .dynamic
-SectionHeaderTable:
-  NoHeaders: true

diff  --git a/llvm/test/tools/llvm-readobj/ELF/file-headers.test b/llvm/test/tools/llvm-readobj/ELF/file-headers.test
index 4b78007c0f4e..97ab9f092b22 100644
--- a/llvm/test/tools/llvm-readobj/ELF/file-headers.test
+++ b/llvm/test/tools/llvm-readobj/ELF/file-headers.test
@@ -215,8 +215,9 @@ FileHeader:
 ## The index of the section name string table is too large.
 ## The section would be past the EOF.
   EShStrNdx: [[SHSTRNDX=0x3000]]
-SectionHeaderTable:
-  NoHeaders: true
+Sections:
+  - Type: SectionHeaderTable
+    NoHeaders: true
 
 ## Check we don't dump anything except the file header when the section header table can't be read.
 

diff  --git a/llvm/test/tools/llvm-readobj/ELF/hash-table.test b/llvm/test/tools/llvm-readobj/ELF/hash-table.test
index ad52a98639be..db68ebdbcc6e 100644
--- a/llvm/test/tools/llvm-readobj/ELF/hash-table.test
+++ b/llvm/test/tools/llvm-readobj/ELF/hash-table.test
@@ -113,8 +113,8 @@ Sections:
         Value: 0x0
       - Tag:   DT_NULL
         Value: 0x0
-SectionHeaderTable:
-  NoHeaders: [[NOHEADERS=false]]
+  - Type: SectionHeaderTable
+    NoHeaders: [[NOHEADERS=false]]
 ProgramHeaders:
   - Type:     PT_LOAD
     FirstSec: .hash

diff  --git a/llvm/test/tools/llvm-readobj/ELF/malformed-pt-dynamic.test b/llvm/test/tools/llvm-readobj/ELF/malformed-pt-dynamic.test
index 02df7117a832..0666674b43d5 100644
--- a/llvm/test/tools/llvm-readobj/ELF/malformed-pt-dynamic.test
+++ b/llvm/test/tools/llvm-readobj/ELF/malformed-pt-dynamic.test
@@ -137,10 +137,10 @@ Sections:
     Entries:
       - Tag:   DT_NULL
         Value: 0
+  - Type: SectionHeaderTable
+    NoHeaders: [[NOHEADERS=false]]
 ProgramHeaders:
   - Type:     PT_DYNAMIC
     FileSize: [[FILESIZE=<none>]]
     FirstSec: .dynamic
     LastSec:  .dynamic
-SectionHeaderTable:
-  NoHeaders: [[NOHEADERS=false]]

diff  --git a/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test b/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test
index 7ee9c2c0296a..493222adc302 100644
--- a/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test
+++ b/llvm/test/tools/llvm-readobj/ELF/symtab-shndx.test
@@ -313,6 +313,8 @@ Sections:
     Entries: [ 0, 1 ]
     Offset:  0x2000
     Address: 0x2000
+  - Type:      SectionHeaderTable
+    NoHeaders: true
 DynamicSymbols:
   - Name:  dynsym1
     Index: SHN_XINDEX
@@ -327,5 +329,3 @@ ProgramHeaders:
     VAddr:    0x1000
     FirstSec: .dynamic
     LastSec:  .dynamic
-SectionHeaderTable:
-  NoHeaders: true

diff  --git a/llvm/test/tools/obj2yaml/ELF/offset.yaml b/llvm/test/tools/obj2yaml/ELF/offset.yaml
index 1eb464c9839b..47970a07535c 100644
--- a/llvm/test/tools/obj2yaml/ELF/offset.yaml
+++ b/llvm/test/tools/obj2yaml/ELF/offset.yaml
@@ -36,17 +36,17 @@
 #   BASIC-NEXT:     Type:         SHT_PROGBITS
 #   BASIC-NEXT:     AddressAlign: 0x100000000
 #   BASIC-NEXT:     Offset:       0x210
-# HEADERS-NEXT: SectionHeaderTable:
-# HEADERS-NEXT:   Sections:
-# HEADERS-NEXT:     - Name:            .bar4
-# HEADERS-NEXT:     - Name:            .bar3
-# HEADERS-NEXT:     - Name:            .bar2
-# HEADERS-NEXT:     - Name:            .bar1
-# HEADERS-NEXT:     - Name:            .foo3
-# HEADERS-NEXT:     - Name:            .foo2
-# HEADERS-NEXT:     - Name:            .foo1
-# HEADERS-NEXT:     - Name:            .strtab
-# HEADERS-NEXT:     - Name:            .shstrtab
+# HEADERS-NEXT:   - Type: SectionHeaderTable
+# HEADERS-NEXT:     Sections:
+# HEADERS-NEXT:       - Name: .bar4
+# HEADERS-NEXT:       - Name: .bar3
+# HEADERS-NEXT:       - Name: .bar2
+# HEADERS-NEXT:       - Name: .bar1
+# HEADERS-NEXT:       - Name: .foo3
+# HEADERS-NEXT:       - Name: .foo2
+# HEADERS-NEXT:       - Name: .foo1
+# HEADERS-NEXT:       - Name: .strtab
+# HEADERS-NEXT:       - Name: .shstrtab
 # BASIC-NEXT: ...
 
 --- !ELF
@@ -103,18 +103,18 @@ Sections:
     Type:         SHT_PROGBITS
     AddressAlign: 0x100000000
     Offset:       0x210
-SectionHeaderTable:
-  Sections:
+  - Type: SectionHeaderTable
+    Sections:
 ## By default we have the same order of sections as defined by the "Sections" key.
-    - Name: [[SEC1=.foo1]]
-    - Name: [[SEC2=.foo2]]
-    - Name: [[SEC3=.foo3]]
-    - Name: [[SEC4=.bar1]]
-    - Name: [[SEC5=.bar2]]
-    - Name: [[SEC6=.bar3]]
-    - Name: [[SEC7=.bar4]]
-    - Name: .strtab
-    - Name: .shstrtab
+      - Name: [[SEC1=.foo1]]
+      - Name: [[SEC2=.foo2]]
+      - Name: [[SEC3=.foo3]]
+      - Name: [[SEC4=.bar1]]
+      - Name: [[SEC5=.bar2]]
+      - Name: [[SEC6=.bar3]]
+      - Name: [[SEC7=.bar4]]
+      - Name: .strtab
+      - Name: .shstrtab
 
 ## In this case we change the order of sections in the section header table.
 ## Check that we still dump offsets correctly.

diff  --git a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml
index 45b339853cf1..ce19696a35a9 100644
--- a/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/section-headers-exclude.yaml
@@ -32,13 +32,13 @@ Sections:
     Type: SHT_PROGBITS
   - Name: .bar
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: [[INCLUDED]]
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: [[EXCLUDED]]
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: [[INCLUDED]]
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: [[EXCLUDED]]
 
 ## Check we report an error when a section is in both the "Sections" and "Excluded" lists at the same time.
 ## Also check that we report an error if a section is missing from the lists.
@@ -66,13 +66,14 @@ FileHeader:
   Class: ELFCLASS64
   Data:  ELFDATA2LSB
   Type:  ET_REL
-SectionHeaderTable:
-  Sections:
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .strtab
-    - Name: .strtab
+Sections:
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .strtab
+      - Name: .strtab
 
 ## Check that we are able to exclude all sections, except the implicit
 ## null section, with the use of the "Excluded" key.
@@ -91,11 +92,12 @@ FileHeader:
   Class: ELFCLASS64
   Data:  ELFDATA2LSB
   Type:  ET_REL
-SectionHeaderTable:
-  Sections: []
-  Excluded:
-    - Name: .strtab
-    - Name: .shstrtab
+Sections:
+  - Type: SectionHeaderTable
+    Sections: []
+    Excluded:
+      - Name: .strtab
+      - Name: .shstrtab
 
 ## Case B: the "Sections" key is not present.
 # RUN: yaml2obj %s --docnum=4 -o %t4
@@ -106,10 +108,11 @@ FileHeader:
   Class: ELFCLASS64
   Data:  ELFDATA2LSB
   Type:  ET_REL
-SectionHeaderTable:
-  Excluded:
-    - Name: .strtab
-    - Name: .shstrtab
+Sections:
+  - Type: SectionHeaderTable
+    Excluded:
+      - Name: .strtab
+      - Name: .shstrtab
 
 ## Check how we handle cases when a section is excluded, but its section index is needed.
 ## The general rule is: when a section is explicitly linked with another section, which is
@@ -134,13 +137,13 @@ Sections:
     Link: .bar
   - Name: .bar
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: .foo
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .bar
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .foo
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .bar
 
 ## Case B.1: check we report an error when a symbol table section has a Link field which
 ##           points to an excluded section.
@@ -160,13 +163,13 @@ Sections:
     Link:  .foo
   - Name:  .foo
     Type:  SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: [[NAME]]
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .foo
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: [[NAME]]
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .foo
 
 ## Case B.2: check we do not link .dynsym with .dynstr implicitly when the latter is excluded.
 # RUN: yaml2obj %s --docnum=7 -o %t5
@@ -185,13 +188,13 @@ Sections:
     Type:  SHT_DYNSYM
   - Name:  .dynstr
     Type:  SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: .dynsym
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .dynstr
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .dynsym
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .dynstr
 
 ## Case B.3: check we do not link .symtab with .strtab implicitly when the latter is excluded.
 # RUN: yaml2obj %s --docnum=8 -o %t6
@@ -210,12 +213,12 @@ Sections:
     Type:  SHT_SYMTAB
   - Name:  .strtab
     Type:  SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: .symtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .strtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .symtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .strtab
 
 ## Case C: check we report an error when a debug section has a Link field which
 ##         points to an excluded section.
@@ -231,12 +234,12 @@ Sections:
   - Name:  .debug_unknown
     Type:  SHT_PROGBITS
     Link:  .strtab
-SectionHeaderTable:
-  Sections:
-    - Name: .debug_unknown
-    - Name: .shstrtab
-  Excluded:
-    - Name: .strtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .debug_unknown
+      - Name: .shstrtab
+    Excluded:
+      - Name: .strtab
 
 ## Case D.1: check we report an error when a relocatable section has an Info field which
 ##           points to an excluded section.
@@ -253,12 +256,12 @@ Sections:
     Type: SHT_RELA
     Info: .strtab
     Relocations: []
-SectionHeaderTable:
-  Sections:
-    - Name: .rela
-    - Name: .shstrtab
-  Excluded:
-    - Name: .strtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .rela
+      - Name: .shstrtab
+    Excluded:
+      - Name: .strtab
 
 ## Case D.2: check we report an error when the SHT_REL[A] section is linked
 ##           with an excluded section explicitly.
@@ -277,13 +280,13 @@ Sections:
     Relocations: []
   - Name: .symtab
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: .rela
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .symtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .rela
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .symtab
 
 ## Case E: check we report an error when a symbol references an excluded section.
 # RUN: not yaml2obj %s --docnum=12 -o /dev/null 2>&1 | \
@@ -299,17 +302,17 @@ FileHeader:
 Sections:
   - Name: .foo
     Type: SHT_PROGBITS
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .symtab
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .foo
 Symbols:
   - Name:    foo
     Type:    STT_OBJECT
     Section: .foo
-SectionHeaderTable:
-  Sections:
-    - Name: .symtab
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .foo
 
 ## Case F.1: check we report an error when a group section
 ##           contains an excluded section member.
@@ -326,12 +329,12 @@ Sections:
     Type: SHT_GROUP
     Members:
       - SectionOrType: .strtab
-SectionHeaderTable:
-  Sections:
-    - Name: .group
-    - Name: .shstrtab
-  Excluded:
-    - Name: .strtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .group
+      - Name: .shstrtab
+    Excluded:
+      - Name: .strtab
 
 ## Case F.2: check we report an error when the group section is linked
 ##           to an excluded section explicitly.
@@ -350,13 +353,13 @@ Sections:
     Members: []
   - Name: .symtab
     Type: SHT_SYMTAB
-SectionHeaderTable:
-  Sections:
-    - Name: .group
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .symtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .group
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .symtab
 
 ## Case G: check we do not link SHT_LLVM_CALL_GRAPH_PROFILE/SHT_LLVM_ADDRSIG/SHT_GROUP/SHT_REL[A] sections
 ##         with .symtab implicitly when the latter is excluded.
@@ -389,16 +392,16 @@ Sections:
     Relocations: []
   - Name: .symtab
     Type: SHT_SYMTAB
-SectionHeaderTable:
-  Sections:
-    - Name: .cgp
-    - Name: .llvm_addrsig
-    - Name: .group
-    - Name: .rela
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .symtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .cgp
+      - Name: .llvm_addrsig
+      - Name: .group
+      - Name: .rela
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .symtab
 
 ## Case H: check we do not link SHT_HASH/SHT_GNU_HASH sections with .dynsym
 ##         implicitly when the latter is excluded.
@@ -423,14 +426,14 @@ Sections:
     Content: ""
   - Name: .dynsym
     Type: SHT_DYNSYM
-SectionHeaderTable:
-  Sections:
-    - Name: .hash
-    - Name: .gnu_hash
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .dynsym
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .hash
+      - Name: .gnu_hash
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .dynsym
 
 ## Case I: document the case when an excluded section is explicitly linked to another excluded section.
 ##         We report an error in this case, because:
@@ -454,13 +457,13 @@ Sections:
   - Name: .bar
     Type: SHT_PROGBITS
     Link: .foo
-SectionHeaderTable:
-  Sections:
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .foo
-    - Name: .bar
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .foo
+      - Name: .bar
 
 ## Check we set e_shstrndx field to 0 when the section header string table is excluded.
 ## Check that the e_shnum field is adjusted properly when a section is removed.
@@ -475,22 +478,24 @@ FileHeader:
   Class: ELFCLASS64
   Data:  ELFDATA2LSB
   Type:  ET_REL
-SectionHeaderTable:
-  Sections:
-    - Name: .strtab
-  Excluded:
-    - Name: .shstrtab
+Sections:
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .strtab
+    Excluded:
+      - Name: .shstrtab
 
 ## Check we do not allow using "Excluded" together with "NoHeaders".
 # RUN: not yaml2obj %s --docnum=19 -DNOHEADERS=true -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOHEADERS
 # RUN: not yaml2obj %s --docnum=19 -DNOHEADERS=false -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOHEADERS
-# NOHEADERS: NoHeaders can't be used together with Sections/Excluded
+# NOHEADERS: NoHeaders can't be used together with Offset/Sections/Excluded
 
 --- !ELF
 FileHeader:
   Class: ELFCLASS64
   Data:  ELFDATA2LSB
   Type:  ET_REL
-SectionHeaderTable:
-  NoHeaders: [[NOHEADERS]]
-  Excluded:  []
+Sections:
+  - Type: SectionHeaderTable
+    NoHeaders: [[NOHEADERS]]
+    Excluded:  []

diff  --git a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml
index 7eadb4a881b3..5fe029f42e75 100644
--- a/llvm/test/tools/yaml2obj/ELF/section-headers.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/section-headers.yaml
@@ -34,13 +34,13 @@ Sections:
   - Name: .section.foo
     Type: SHT_PROGBITS
     Size: 0x40
-SectionHeaderTable:
-  Sections:
-    - Name: [[SEC1]]
-    - Name: [[SEC2]]
-    - Name: [[SEC3]]
-    - Name: .strtab
-    - Name: .shstrtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: [[SEC1]]
+      - Name: [[SEC2]]
+      - Name: [[SEC3]]
+      - Name: .strtab
+      - Name: .shstrtab
 
 ## Show we are able to reorder sections.
 # RUN: yaml2obj %s -o %t2 -DSEC3=".section (1)" -DSEC2=".section (2)" -DSEC1=".section.foo"
@@ -93,8 +93,8 @@ FileHeader:
 Sections:
   - Name: .foo
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections: []
+  - Type: SectionHeaderTable
+    Sections: []
 
 ## Test that we are able to use "NoHeaders" property to produce an empty section header table.
 # RUN: yaml2obj %s --docnum=3 -DNOHEADERS=true -o %t3.1
@@ -113,8 +113,8 @@ FileHeader:
 Sections:
   - Name: .foo
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  NoHeaders: [[NOHEADERS]]
+  - Type: SectionHeaderTable
+    NoHeaders: [[NOHEADERS]]
 
 ## Test that we are able to set NoHeaders to false. In this case the tool produces an output
 ## as if there were no `SectionHeaderTable` key at all.
@@ -137,7 +137,7 @@ SectionHeaderTable:
 # RUN: not yaml2obj %s --docnum=4 -DNOHEADERS=true -o /dev/null 2>&1 | FileCheck %s --check-prefix=SECTIONS-NO-HEADERS
 # RUN: not yaml2obj %s --docnum=4 -DNOHEADERS=false -o /dev/null 2>&1 | FileCheck %s --check-prefix=SECTIONS-NO-HEADERS
 
-# SECTIONS-NO-HEADERS: error: NoHeaders can't be used together with Sections/Excluded
+# SECTIONS-NO-HEADERS: error: NoHeaders can't be used together with Offset/Sections/Excluded
 
 --- !ELF
 FileHeader:
@@ -147,12 +147,12 @@ FileHeader:
 Sections:
   - Name: .foo
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections:  []
-  NoHeaders: [[NOHEADERS]]
+  - Type: SectionHeaderTable
+    Sections:  []
+    NoHeaders: [[NOHEADERS]]
 
 ## Check that we do not allow an empty SectionHeaderTable tag and suggest to use an explicit syntax instead.
-# RUN: not yaml2obj %s --docnum=5 -DVAL="" -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO-VALUE
+# RUN: not yaml2obj %s --docnum=5 -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO-VALUE
 
 # NO-VALUE: SectionHeaderTable can't be empty. Use 'NoHeaders' key to drop the section header table
 
@@ -164,12 +164,7 @@ FileHeader:
 Sections:
   - Name: .foo
     Type: SHT_PROGBITS
-SectionHeaderTable: [[VAL]]
-
-## An empty mapping is also not allowed for the SectionHeaderTable tag.
-# RUN: not yaml2obj %s --docnum=5 -DVAL="[]" -o /dev/null 2>&1 | FileCheck %s --check-prefix=NOT-A-MAPPING
-
-# NOT-A-MAPPING: error: not a mapping
+  - Type: SectionHeaderTable
 
 ## Test that we are still able to override e_shoff, e_shnum and e_shstrndx
 ## fields even when we do not produce section headers.
@@ -188,8 +183,9 @@ FileHeader:
   EShOff:    0x2
   EShNum:    0x3
   EShStrNdx: 0x4
-SectionHeaderTable:
-  NoHeaders: true
+Sections:
+  - Type:      SectionHeaderTable
+    NoHeaders: true
 
 ## Check that section indices are updated properly in other places when we
 ## reorder sections in the section header table.
@@ -233,15 +229,15 @@ Sections:
   - Name: .another.2
     Link: .bar
     Type: SHT_PROGBITS
-SectionHeaderTable:
-  Sections:
-    - Name: [[SEC1]]
-    - Name: [[SEC2]]
-    - Name: .another.1
-    - Name: .another.2
-    - Name: .symtab
-    - Name: .strtab
-    - Name: .shstrtab
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: [[SEC1]]
+      - Name: [[SEC2]]
+      - Name: .another.1
+      - Name: .another.2
+      - Name: .symtab
+      - Name: .strtab
+      - Name: .shstrtab
 Symbols:
   - Name:    foo
     Section: .foo
@@ -275,5 +271,142 @@ Sections:
 ## for writing the section header table.
     Size:   0xFF
     Offset: 0x100
-SectionHeaderTable:
-  NoHeaders: true
+  - Type:      SectionHeaderTable
+    NoHeaders: true
+
+## Check we do not allow using "Offset" together with "NoHeaders".
+# RUN: not yaml2obj %s --docnum=9 -DNOHEADERS=true -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO-HEADERS-OFFSET
+# RUN: not yaml2obj %s --docnum=9 -DNOHEADERS=false -o /dev/null 2>&1 | FileCheck %s --check-prefix=NO-HEADERS-OFFSET
+
+# NO-HEADERS-OFFSET: error: NoHeaders can't be used together with Offset/Sections/Excluded
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_REL
+Sections:
+  - Name: .foo
+    Type: SHT_PROGBITS
+  - Type:      SectionHeaderTable
+    Offset:    0x1000
+    NoHeaders: [[NOHEADERS]]
+
+## Check we can place the section header table before other sections.
+
+# RUN: yaml2obj %s --docnum=10 -o %t10
+# RUN: llvm-readelf --file-headers --sections %t10 | FileCheck %s --check-prefix=PLACE-BEFORE
+
+# PLACE-BEFORE: Start of section headers:  64 (bytes into file)
+# PLACE-BEFORE: Size of section headers:   64 (bytes)
+# PLACE-BEFORE: Number of section headers: 4
+
+# PLACE-BEFORE: Section Headers:
+# PLACE-BEFORE:   [Nr] Name Type     Address          Off    Size
+## 0x140 == Start of section headers (64) + Size of section headers (64) * Number of section headers (4);
+# PLACE-BEFORE:   [ 1] .foo PROGBITS 0000000000000000 000140 000000
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_REL
+Sections:
+  - Type:   SectionHeaderTable
+    Offset: [[OFFSET=<none>]]
+    Sections:
+      - Name: .foo
+      - Name: .strtab
+      - Name: .shstrtab
+  - Name: .foo
+    Type: SHT_PROGBITS
+
+## Check we can use the Offset key to place the section header table at a 
diff erent location.
+
+# RUN: yaml2obj %s --docnum=10 -DOFFSET=0x1000 -o %t10.offset
+# RUN: llvm-readelf --file-headers --sections %t10.offset | \
+# RUN:   FileCheck %s --check-prefix=PLACE-BEFORE-OFFSET
+
+# PLACE-BEFORE-OFFSET: Start of section headers:          4096 (bytes into file)
+# PLACE-BEFORE-OFFSET: Size of section headers:           64 (bytes)
+# PLACE-BEFORE-OFFSET: Number of section headers:         4
+
+# PLACE-BEFORE-OFFSET: Section Headers:
+# PLACE-BEFORE-OFFSET:   [Nr] Name Type     Address          Off    Size
+## 0x1100 == Start of section headers (0x1000) + Size of section headers (64) * Number of section headers (4);
+# PLACE-BEFORE-OFFSET:   [ 1] .foo PROGBITS 0000000000000000 001100 000000
+
+## Check we can place the section header table somewhere in the middle of the sections list.
+
+# RUN: yaml2obj %s --docnum=11 -o %t11
+# RUN: llvm-readelf --sections %t11 | FileCheck %s --check-prefix=PLACE-AT-MIDDLE
+
+# PLACE-AT-MIDDLE: There are 5 section headers, starting at offset 0x140:
+# PLACE-AT-MIDDLE:      [Nr] Name      Type     Address          Off    Size
+# PLACE-AT-MIDDLE:      [ 1] .foo      PROGBITS 0000000000000000 000040 000100
+## The section header table is placed here, at 0x40 + 0x100 == 0x140.
+## The size of the table is 5 * 64 == 320 == 0x140.
+## 0x140 + 0x140 == offset of the .bar section.
+# PLACE-AT-MIDDLE-NEXT: [ 2] .bar      PROGBITS 0000000000000000 000280 000200
+# PLACE-AT-MIDDLE-NEXT: [ 3] .strtab   STRTAB   0000000000000000 000480 000001
+# PLACE-AT-MIDDLE-NEXT: [ 4] .shstrtab STRTAB   0000000000000000 000481 00001d
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_REL
+Sections:
+  - Name: .foo
+    Type: SHT_PROGBITS
+    Size: 0x100
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .foo
+      - Name: .bar
+      - Name: .strtab
+      - Name: .shstrtab
+  - Name: .bar
+    Type: SHT_PROGBITS
+    Size: 0x200
+
+## Check we don`t allow having multiple SectionHeaderTable chunks.
+
+# RUN: not yaml2obj %s --docnum=12 -o /dev/null 2>&1 | FileCheck %s --check-prefix=MULTIPLE
+
+# MULTIPLE: error: multiple section header tables are not allowed
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_REL
+Sections:
+  - Type:     SectionHeaderTable
+    Sections: []
+  - Type:     SectionHeaderTable
+    Sections: []
+
+## Check that we place implicit sections before the section header table
+## when it is placed at the end explicitly.
+
+# RUN: yaml2obj %s --docnum=13 -o %t13
+# RUN: llvm-readelf --sections %t13 | FileCheck %s --check-prefix=IMPLICIT
+
+# IMPLICIT:      There are 3 section headers, starting at offset 0x58:
+# IMPLICIT:      [Nr] Name      Type   Address          Off    Size
+# IMPLICIT:      [ 1] .strtab   STRTAB 0000000000000000 000040 000001
+# IMPLICIT-NEXT: [ 2] .shstrtab STRTAB 0000000000000000 000041 000013
+## The section header table is placed here, at 0x58.
+## alignTo(8, 0x41 + 0x13) == 0x58.
+
+--- !ELF
+FileHeader:
+  Class: ELFCLASS64
+  Data:  ELFDATA2LSB
+  Type:  ET_REL
+Sections:
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .strtab
+      - Name: .shstrtab

diff  --git a/llvm/test/tools/yaml2obj/ELF/verdef-section.yaml b/llvm/test/tools/yaml2obj/ELF/verdef-section.yaml
index 65490da101c6..fe311c302ae2 100644
--- a/llvm/test/tools/yaml2obj/ELF/verdef-section.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/verdef-section.yaml
@@ -241,10 +241,10 @@ Sections:
     Type: SHT_GNU_verdef
   - Name: .dynstr
     Type: SHT_STRTAB
-SectionHeaderTable:
-  Sections:
-    - Name: .gnu.version_d
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .dynstr
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .gnu.version_d
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .dynstr

diff  --git a/llvm/test/tools/yaml2obj/ELF/verneed-section.yaml b/llvm/test/tools/yaml2obj/ELF/verneed-section.yaml
index 2fbf83a69b4e..7f378476e06f 100644
--- a/llvm/test/tools/yaml2obj/ELF/verneed-section.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/verneed-section.yaml
@@ -182,10 +182,10 @@ Sections:
     Type: SHT_GNU_verneed
   - Name: .dynstr
     Type: SHT_STRTAB
-SectionHeaderTable:
-  Sections:
-    - Name: .gnu.version_r
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .dynstr
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .gnu.version_r
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .dynstr

diff  --git a/llvm/test/tools/yaml2obj/ELF/versym-section.yaml b/llvm/test/tools/yaml2obj/ELF/versym-section.yaml
index 24a0916a0ef8..4be29c20f172 100644
--- a/llvm/test/tools/yaml2obj/ELF/versym-section.yaml
+++ b/llvm/test/tools/yaml2obj/ELF/versym-section.yaml
@@ -194,10 +194,10 @@ Sections:
     Type: SHT_GNU_versym
   - Name: .dynsym
     Type: SHT_DYNSYM
-SectionHeaderTable:
-  Sections:
-    - Name: .gnu.version
-    - Name: .strtab
-    - Name: .shstrtab
-  Excluded:
-    - Name: .dynsym
+  - Type: SectionHeaderTable
+    Sections:
+      - Name: .gnu.version
+      - Name: .strtab
+      - Name: .shstrtab
+    Excluded:
+      - Name: .dynsym

diff  --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index d92e2254d9b3..c85e2653b655 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -382,13 +382,18 @@ template <class ELFT> Expected<ELFYAML::Object *> ELFDumper<ELFT>::dump() {
                cast<ELFYAML::Section>(B.get())->OriginalSecNdx;
       });
   if (!SectionsSorted) {
-    Y->SectionHeaders.emplace();
-    Y->SectionHeaders->Sections.emplace();
+    std::unique_ptr<ELFYAML::SectionHeaderTable> SHT =
+        std::make_unique<ELFYAML::SectionHeaderTable>(/*IsImplicit=*/false);
+    SHT->Sections.emplace();
     for (ELFYAML::Section *S : OriginalOrder)
-      Y->SectionHeaders->Sections->push_back({S->Name});
+      SHT->Sections->push_back({S->Name});
+    Chunks.push_back(std::move(SHT));
   }
 
   llvm::erase_if(Chunks, [this, &Y](const std::unique_ptr<ELFYAML::Chunk> &C) {
+    if (isa<ELFYAML::SectionHeaderTable>(*C.get()))
+      return false;
+
     const ELFYAML::Section &S = cast<ELFYAML::Section>(*C.get());
     return !shouldPrintSection(S, Sections[S.OriginalSecNdx], Y->DWARF);
   });


        


More information about the llvm-branch-commits mailing list