[llvm] [ELFYAML] Introduce `CustomRawContent` (PR #115707)

via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 11 02:38:39 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-support

Author: NAKAMURA Takumi (chapuni)

<details>
<summary>Changes</summary>

`CustomRawContent` itself is an abstract class. Each of its derivables can have customized encoder and decoder. At the moment, their type assumes `SHT_PROGBITS`.

Introduce `ELFYAML::Opt` (and `yaml::Opt`) to define parameters, hooks, and contexts.

Since it is pluggable, the definition of `CustomRawContent` derivables can be located not in `LLVMObjectYAML` but in YAML tools like `obj2yaml` and `yaml2obj`. This commit shows also skeletons in YAML tools.

---
Full diff: https://github.com/llvm/llvm-project/pull/115707.diff


7 Files Affected:

- (modified) llvm/include/llvm/ObjectYAML/ELFYAML.h (+69-1) 
- (modified) llvm/include/llvm/Support/YAMLTraits.h (+17) 
- (modified) llvm/lib/ObjectYAML/ELFEmitter.cpp (+15-1) 
- (modified) llvm/lib/ObjectYAML/ELFYAML.cpp (+23-1) 
- (modified) llvm/lib/Support/YAMLTraits.cpp (+8) 
- (modified) llvm/tools/obj2yaml/elf2yaml.cpp (+63) 
- (modified) llvm/tools/yaml2obj/yaml2obj.cpp (+16) 


``````````diff
diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h
index 8f045d6383623b..ee8ca11aa2ee62 100644
--- a/llvm/include/llvm/ObjectYAML/ELFYAML.h
+++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h
@@ -211,6 +211,7 @@ struct Chunk {
     Dynamic,
     Group,
     RawContent,
+    CustomRawContent,
     Relocation,
     Relr,
     NoBits,
@@ -388,7 +389,7 @@ struct DynamicSection : Section {
 struct RawContentSection : Section {
   std::optional<llvm::yaml::Hex64> Info;
 
-  RawContentSection() : Section(ChunkKind::RawContent) {}
+  RawContentSection(ChunkKind Kind = ChunkKind::RawContent) : Section(Kind) {}
 
   static bool classof(const Chunk *S) {
     return S->Kind == ChunkKind::RawContent;
@@ -398,6 +399,29 @@ struct RawContentSection : Section {
   std::optional<std::vector<uint8_t>> ContentBuf;
 };
 
+/// Abstract base class for non-blob contents.
+struct CustomRawContentSection : RawContentSection {
+  CustomRawContentSection() : RawContentSection(ChunkKind::CustomRawContent) {
+    // Type assumes PROGBITS.
+    Type = ELF::SHT_PROGBITS;
+    Flags = ELF::SHF_GNU_RETAIN;
+    AddressAlign = 1;
+  }
+
+  /// Apply mappings.
+  virtual void sectionMapping(yaml::IO &IO) = 0;
+
+  /// Decode Content and store to members.
+  virtual Error decode(const ArrayRef<uint8_t> Content, bool isLE) = 0;
+
+  /// Encode members and returns Content.
+  virtual std::string encode() const = 0;
+
+  static bool classof(const Chunk *S) {
+    return S->Kind == ChunkKind::CustomRawContent;
+  }
+};
+
 struct NoBitsSection : Section {
   NoBitsSection() : Section(ChunkKind::NoBits) {}
 
@@ -757,6 +781,50 @@ struct Object {
 bool shouldAllocateFileSpace(ArrayRef<ProgramHeader> Phdrs,
                              const NoBitsSection &S);
 
+/// ELFYAML::Opt -- Abstract base class for ELFYAML to provide
+/// the interface for handling CustomRawConetentSection.
+///
+/// Users in ELFYAML should obtain the pointer with
+/// dyn_cast<ELFYAML::Opt> if IO::Opt is the instance from yaml::Opt.
+///
+///     if (auto *Opt = dyn_cast<ELFYAML::Opt>(IO.Opt))
+///
+/// Derivered classes should not modify OptClassID to ensue that
+/// dyn_cast<ELFYAML::Opt> can find this interface.
+class Opt : public yaml::Opt {
+public:
+  Opt() {
+    OptClassID = &ID;
+    ELFOptClassID = &ID;
+  }
+  ~Opt();
+
+  /// Create an empty new object of CustomRawContentSection.
+  /// Its contents will be filled later.
+  /// This is called:
+  ///   - Before preMapping for elf2yaml.
+  ///   - After preMapping for yaml2elf.
+  virtual std::unique_ptr<CustomRawContentSection>
+  makeCustomRawContentSection(StringRef Name) const = 0;
+
+  /// Called before mapping sections for prettyprinting yaml.
+  virtual void preMapping(const ELFYAML::Object &Object, bool IsOutputting) = 0;
+
+  /// Called after mapping sections to gather members for the file format.
+  virtual void postMapping(const ELFYAML::Object &Object,
+                           bool IsOutputting) = 0;
+
+  /// Tell IO::Opt to be this and derivered classes.
+  static bool classof(const yaml::Opt *Obj) { return (Obj->OptClassID == &ID); }
+
+  /// This will be not needed unless the pointer to ELFYAML::Opt would
+  /// be cast further.
+  static bool classof(const Opt *Obj) { return (Obj->ELFOptClassID == &ID); }
+  const char *ELFOptClassID;
+
+private:
+  static const char ID;
+};
 } // end namespace ELFYAML
 } // end namespace llvm
 
diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h
index 1d04783753d5cd..1f1e06a9e37735 100644
--- a/llvm/include/llvm/Support/YAMLTraits.h
+++ b/llvm/include/llvm/Support/YAMLTraits.h
@@ -39,6 +39,18 @@ class VersionTuple;
 
 namespace yaml {
 
+/// The base class of options.
+class Opt {
+public:
+  virtual ~Opt();
+
+  static bool classof(const Opt *Obj) { return (Obj->OptClassID == &ID); }
+  const char *OptClassID = &ID;
+
+private:
+  static const char ID;
+};
+
 enum class NodeKind : uint8_t {
   Scalar,
   Map,
@@ -964,6 +976,11 @@ class IO {
 
 private:
   void *Ctxt;
+  Opt DefaultOpt;
+
+public:
+  /// This may be overwritten in derivered classes.
+  Opt *Opt = &DefaultOpt;
 };
 
 namespace detail {
diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp
index fc234581a45a70..09a540b0485176 100644
--- a/llvm/lib/ObjectYAML/ELFEmitter.cpp
+++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp
@@ -251,6 +251,9 @@ template <class ELFT> class ELFState {
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::NoBitsSection &Section,
                            ContiguousBlobAccumulator &CBA);
+  void writeSectionContent(Elf_Shdr &SHeader,
+                           const ELFYAML::CustomRawContentSection &Section,
+                           ContiguousBlobAccumulator &CBA);
   void writeSectionContent(Elf_Shdr &SHeader,
                            const ELFYAML::RawContentSection &Section,
                            ContiguousBlobAccumulator &CBA);
@@ -859,7 +862,9 @@ void ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders,
     if (!isa<ELFYAML::NoBitsSection>(Sec) && (Sec->Content || Sec->Size))
       SHeader.sh_size = writeContent(CBA, Sec->Content, Sec->Size);
 
-    if (auto S = dyn_cast<ELFYAML::RawContentSection>(Sec)) {
+    if (auto S = dyn_cast<ELFYAML::CustomRawContentSection>(Sec)) {
+      writeSectionContent(SHeader, *S, CBA);
+    } else if (auto S = dyn_cast<ELFYAML::RawContentSection>(Sec)) {
       writeSectionContent(SHeader, *S, CBA);
     } else if (auto S = dyn_cast<ELFYAML::SymtabShndxSection>(Sec)) {
       writeSectionContent(SHeader, *S, CBA);
@@ -1264,6 +1269,15 @@ void ELFState<ELFT>::writeSectionContent(
     SHeader.sh_info = *Section.Info;
 }
 
+template <class ELFT>
+void ELFState<ELFT>::writeSectionContent(
+    Elf_Shdr &SHeader, const ELFYAML::CustomRawContentSection &Section,
+    ContiguousBlobAccumulator &CBA) {
+  std::string Storage = Section.encode();
+  SHeader.sh_size = Storage.size();
+  CBA.write(Storage.data(), Storage.size());
+}
+
 static bool isMips64EL(const ELFYAML::Object &Obj) {
   return Obj.getMachine() == llvm::ELF::EM_MIPS &&
          Obj.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64) &&
diff --git a/llvm/lib/ObjectYAML/ELFYAML.cpp b/llvm/lib/ObjectYAML/ELFYAML.cpp
index 89ffc383a4a6ec..7d06c2c32e23b8 100644
--- a/llvm/lib/ObjectYAML/ELFYAML.cpp
+++ b/llvm/lib/ObjectYAML/ELFYAML.cpp
@@ -29,6 +29,9 @@ namespace llvm {
 
 ELFYAML::Chunk::~Chunk() = default;
 
+ELFYAML::Opt::~Opt() = default;
+const char ELFYAML::Opt::ID = 'E';
+
 namespace ELFYAML {
 ELF_ELFOSABI Object::getOSAbi() const { return Header.OSABI; }
 
@@ -1582,6 +1585,19 @@ static bool isInteger(StringRef Val) {
 
 void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
     IO &IO, std::unique_ptr<ELFYAML::Chunk> &Section) {
+  if (!IO.outputting()) {
+    /// Prepare CustomRawContentSection by Name for ELFEmitter.
+    if (auto *Opt = dyn_cast<ELFYAML::Opt>(IO.Opt)) {
+      StringRef Name;
+      IO.mapOptional("Name", Name);
+      if (auto S = Opt->makeCustomRawContentSection(Name)) {
+        S->sectionMapping(IO);
+        Section = std::move(S);
+        return;
+      }
+    }
+  }
+
   ELFYAML::ELF_SHT Type;
   StringRef TypeStr;
   if (IO.outputting()) {
@@ -1731,7 +1747,9 @@ void MappingTraits<std::unique_ptr<ELFYAML::Chunk>>::mapping(
         Section = std::make_unique<ELFYAML::RawContentSection>();
     }
 
-    if (auto S = dyn_cast<ELFYAML::RawContentSection>(Section.get()))
+    if (auto S = dyn_cast<ELFYAML::CustomRawContentSection>(Section.get()))
+      S->sectionMapping(IO);
+    else if (auto S = dyn_cast<ELFYAML::RawContentSection>(Section.get()))
       sectionMapping(IO, *S);
     else
       sectionMapping(IO, *cast<ELFYAML::StackSizesSection>(Section.get()));
@@ -1981,6 +1999,8 @@ void MappingTraits<ELFYAML::ARMIndexTableEntry>::mapping(
 void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) {
   assert(!IO.getContext() && "The IO context is initialized already");
   IO.setContext(&Object);
+  if (auto *Opt = dyn_cast<ELFYAML::Opt>(IO.Opt))
+    Opt->preMapping(Object, IO.outputting());
   IO.mapTag("!ELF", true);
   IO.mapRequired("FileHeader", Object.Header);
   IO.mapOptional("ProgramHeaders", Object.ProgramHeaders);
@@ -1994,6 +2014,8 @@ void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) {
     Object.DWARF->Is64BitAddrSize =
         Object.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64);
   }
+  if (auto *Opt = dyn_cast<ELFYAML::Opt>(IO.Opt))
+    Opt->postMapping(Object, IO.outputting());
   IO.setContext(nullptr);
 }
 
diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp
index 56b557646100b1..4c5d8d5a138aa4 100644
--- a/llvm/lib/Support/YAMLTraits.cpp
+++ b/llvm/lib/Support/YAMLTraits.cpp
@@ -31,6 +31,14 @@
 using namespace llvm;
 using namespace yaml;
 
+//===----------------------------------------------------------------------===//
+//  Opt
+//===----------------------------------------------------------------------===//
+
+Opt::~Opt() = default;
+
+const char Opt::ID = '@';
+
 //===----------------------------------------------------------------------===//
 //  IO
 //===----------------------------------------------------------------------===//
diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp
index 9b4644bde36c0b..3b3fc0f9e8b3c1 100644
--- a/llvm/tools/obj2yaml/elf2yaml.cpp
+++ b/llvm/tools/obj2yaml/elf2yaml.cpp
@@ -22,6 +22,19 @@
 using namespace llvm;
 
 namespace {
+class DumperOpt : public ELFYAML::Opt {
+public:
+  std::unique_ptr<ELFYAML::CustomRawContentSection>
+  makeCustomRawContentSection(StringRef Name) const override {
+    return nullptr;
+  }
+  void preMapping(const ELFYAML::Object &Object, bool IsOutputting) override {
+    // Do nothing.
+  }
+  void postMapping(const ELFYAML::Object &Object, bool IsOutputting) override {
+    // Do nothing.
+  }
+};
 
 template <class ELFT>
 class ELFDumper {
@@ -80,6 +93,8 @@ class ELFDumper {
   Expected<ELFYAML::RelrSection *> dumpRelrSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::RawContentSection *>
   dumpContentSection(const Elf_Shdr *Shdr);
+  Expected<ELFYAML::CustomRawContentSection *>
+  dumpCustomRawContentSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::SymtabShndxSection *>
   dumpSymtabShndxSection(const Elf_Shdr *Shdr);
   Expected<ELFYAML::NoBitsSection *> dumpNoBitsSection(const Elf_Shdr *Shdr);
@@ -104,6 +119,8 @@ class ELFDumper {
                           std::optional<DWARFYAML::Data> DWARF);
 
 public:
+  DumperOpt Opt;
+
   ELFDumper(const object::ELFFile<ELFT> &O, std::unique_ptr<DWARFContext> DCtx);
   Expected<ELFYAML::Object *> dump();
 };
@@ -657,6 +674,18 @@ ELFDumper<ELFT>::dumpSections() {
       if (!NameOrErr)
         return NameOrErr.takeError();
 
+      if (auto ResultOrErr = dumpCustomRawContentSection(&Sec)) {
+        auto *Ptr = *ResultOrErr;
+        if (Ptr) {
+          if (Error E = Add(Ptr))
+            return E;
+          continue;
+        } else {
+          // Do nothing -- nullptr
+        }
+      } else
+        return ResultOrErr.takeError();
+
       if (ELFYAML::StackSizesSection::nameMatches(*NameOrErr)) {
         if (Error E = Add(dumpStackSizesSection(&Sec)))
           return std::move(E);
@@ -1654,6 +1683,39 @@ ELFDumper<ELFT>::dumpMipsABIFlags(const Elf_Shdr *Shdr) {
   return S.release();
 }
 
+template <class ELFT>
+Expected<ELFYAML::CustomRawContentSection *>
+ELFDumper<ELFT>::dumpCustomRawContentSection(const Elf_Shdr *Shdr) {
+  Expected<StringRef> NameOrErr = getUniquedSectionName(*Shdr);
+  if (Error E = NameOrErr.takeError())
+    return nullptr;
+  auto Name = std::move(*NameOrErr);
+
+  auto S = Opt.makeCustomRawContentSection(Name);
+  if (!S)
+    return nullptr;
+
+  if (Error E = dumpCommonSection(Shdr, *S))
+    return std::move(E);
+
+  unsigned SecIndex = Shdr - &Sections[0];
+  if (SecIndex == 0 && Shdr->sh_type == ELF::SHT_NULL)
+    return nullptr;
+
+  auto ContentOrErr = Obj.getSectionContents(*Shdr);
+  if (!ContentOrErr)
+    return ContentOrErr.takeError();
+
+  ArrayRef<uint8_t> Content = *ContentOrErr;
+  if (Content.empty())
+    return nullptr;
+
+  S->Content = yaml::BinaryRef(Content);
+  if (Error E = S->decode(Content, Obj.isLE()))
+    return E;
+  return S.release();
+}
+
 template <class ELFT>
 static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
                       std::unique_ptr<DWARFContext> DWARFCtx) {
@@ -1664,6 +1726,7 @@ static Error elf2yaml(raw_ostream &Out, const object::ELFFile<ELFT> &Obj,
 
   std::unique_ptr<ELFYAML::Object> YAML(YAMLOrErr.get());
   yaml::Output Yout(Out);
+  Yout.Opt = &Dumper.Opt;
   Yout << *YAML;
 
   return Error::success();
diff --git a/llvm/tools/yaml2obj/yaml2obj.cpp b/llvm/tools/yaml2obj/yaml2obj.cpp
index 4a060e1aad427f..3d356dc7a006fc 100644
--- a/llvm/tools/yaml2obj/yaml2obj.cpp
+++ b/llvm/tools/yaml2obj/yaml2obj.cpp
@@ -58,6 +58,20 @@ static cl::opt<uint64_t> MaxSize(
 cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
                                     cl::value_desc("filename"), cl::init("-"),
                                     cl::Prefix, cl::cat(Cat));
+
+class EmitterOpt : public ELFYAML::Opt {
+public:
+  std::unique_ptr<ELFYAML::CustomRawContentSection>
+  makeCustomRawContentSection(StringRef Name) const override {
+    return nullptr;
+  }
+  void preMapping(const ELFYAML::Object &Object, bool IsOutputting) override {
+    // Do nothing.
+  }
+  void postMapping(const ELFYAML::Object &Object, bool IsOutputting) override {
+    // Do nothing.
+  }
+};
 } // namespace
 
 static std::optional<std::string> preprocess(StringRef Buf,
@@ -142,7 +156,9 @@ int main(int argc, char **argv) {
   if (PreprocessOnly) {
     Out->os() << Buffer;
   } else {
+    EmitterOpt Opt;
     yaml::Input YIn(*Buffer);
+    YIn.Opt = &Opt;
 
     if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum,
                      MaxSize == 0 ? UINT64_MAX : MaxSize))

``````````

</details>


https://github.com/llvm/llvm-project/pull/115707


More information about the llvm-commits mailing list