Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ELFYAML] Introduce the external interface CustomSection #115707

Closed
wants to merge 7 commits into from

Conversation

chapuni
Copy link
Contributor

@chapuni chapuni commented Nov 11, 2024

CustomSection itself is an abstract class. Each of its derivables can have customized encoder and decoder.

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

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

https://discourse.llvm.org/t/rfc-objectyaml-with-coverage-map-sections/82953

@llvmbot
Copy link
Member

llvmbot commented Nov 11, 2024

@llvm/pr-subscribers-objectyaml

@llvm/pr-subscribers-llvm-support

Author: NAKAMURA Takumi (chapuni)

Changes

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 --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))

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what you're trying to achieve with this PR, but it certainly isn't how I'd expect a new section type to be supported. The new class should be a sibling class of RawContentSection, not a child class. As stated in the RFC, RawContentSection is intended for use when you are specifying the contents as a binary blob. A new section type that is designed to provide a way to easily describe the sections you discussed in the RFC would not need that functionality.

///
/// Derivered classes should not modify OptClassID to ensue that
/// dyn_cast<ELFYAML::Opt> can find this interface.
class Opt : public yaml::Opt {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does your new class type need a completely different approach to e.g. HashSection or NoteSection?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse me, I haven't understood your suggestion. Let me take more time.

Copy link
Contributor Author

@chapuni chapuni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jh7370 Thanks for your review.

The new class should be a sibling class of RawContentSection, not a child class.

Yes, it makes sense. I started modifying RawContentSection at first, since covmap sections were under it. With my progression, it can be decoupled finally.

The name CustomRawContentSections is odd. I'll rename it to CustomSection or whatever. It isn't able to override non-PROGBITS sections at the moment, though.

Besides, I've updated for the new section can have Type, Flags, etc., aka common. I noticed they couldn't be generalized while I was adopting obj2yaml to non-amd64 ELF binaries.

So, the new CustomSection will handle its custom fields instead of Content.

This aims for ObjectYAML utilities to handle 3rd-party blob formats as plugin. I don't think our covmap is a part of ELF. (In contrast, I think extensions to ELF should be implemented in ObjectYAML.) My implementation was not decoupled but a part of ObjectYAML when I wrote the article.

///
/// Derivered classes should not modify OptClassID to ensue that
/// dyn_cast<ELFYAML::Opt> can find this interface.
class Opt : public yaml::Opt {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excuse me, I haven't understood your suggestion. Let me take more time.

@jh7370
Copy link
Collaborator

jh7370 commented Nov 13, 2024

Hi @chapuni. I think you may be overthinking things. The general pattern for sections with special formats is to have a new class for each such section, in ELFYAML.h, derived from Section, along with potential other helper classes to represent entries (and sub-entries) in that section (see e.g. DynamicEntry, BBAddrMapEntry). These classes are named after the section type they are representing (so e.g. DynamicSection and BBAddrMapSection). They override the getEntries method to describe the members they'll have (GnuHashSection is a good example here, because it has multiple different elements).

Based on your RFC, I'd expect to see something like CovMapSection and CovFunSection, with helper classes for things like Expressions and Records. You'll also need to add LLVM_YAML_IS_SEQUENCE_VECTOR entries for vector-like types and MappingTraits specializations (as with other examples in the header). You'll then need a writeSectionContent overload for each of the new section types and make sure to call these in the right places. There are probably various other that will need updating too, but in essence, just do what the existing code does, rather than trying to reinvent the wheel.

It doesn't matter that these sections aren't part of the general ELF ABI. There are other sections that are in the same boat (e.g. the BBAddrMapSection) that are implemented in these files.

I hope that makes more sense.

@chapuni chapuni changed the title [ELFYAML] Introduce CustomRawContent [ELFYAML] Introduce the external interface CustomSection Nov 14, 2024
@chapuni
Copy link
Contributor Author

chapuni commented Nov 14, 2024

@jh7370 Thank for the explanation.

I have a plan to integrate covmap encoder with LLVMCoverage and YAML sections. I am proposing "plugin" since I want to avoid for LLVMObjectYAML depending on LLVMCoverage. LLVMObjectYAML may be unaware of blob contents defined externally.

Do you think better that LLVMObjectYAML shall import LLVMCoverage stuff?

@ornata
Copy link

ornata commented Dec 13, 2024

I have a plan to integrate covmap encoder with LLVMCoverage and YAML sections. I am proposing "plugin" since I want to avoid for LLVMObjectYAML depending on LLVMCoverage. LLVMObjectYAML may be unaware of blob contents defined externally.

What would the disadvantage of depending on LLVMCoverage be?

@chapuni
Copy link
Contributor Author

chapuni commented Jan 10, 2025

@ornata Imagine that clangParse would depend on X86CodeGen just for parsing MSC style asm. I think we have to avoid adhoc deps as possible and keep clean as the library.

@chapuni chapuni closed this Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants