-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Conversation
@llvm/pr-subscribers-objectyaml @llvm/pr-subscribers-llvm-support Author: NAKAMURA Takumi (chapuni) Changes
Introduce Since it is pluggable, the definition of Full diff: https://github.com/llvm/llvm-project/pull/115707.diff 7 Files Affected:
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))
|
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this 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 { |
There was a problem hiding this comment.
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.
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 Based on your RFC, I'd expect to see something like 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. |
CustomRawContent
CustomSection
@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? |
What would the disadvantage of depending on LLVMCoverage be? |
@ornata Imagine that |
CustomSection
itself is an abstract class. Each of its derivables can have customized encoder and decoder.Introduce
ELFYAML::Opt
(andyaml::IO::OptBase
) to define parameters, hooks, and contexts.Since it is pluggable, the definition of
CustomSection
derivables can be located not inLLVMObjectYAML
but in YAML tools likeobj2yaml
andyaml2obj
. This commit shows also skeletons in YAML tools.https://discourse.llvm.org/t/rfc-objectyaml-with-coverage-map-sections/82953