diff --git a/llvm/include/llvm/ObjectYAML/ELFYAML.h b/llvm/include/llvm/ObjectYAML/ELFYAML.h index 8f045d6383623..38497c813c0fa 100644 --- a/llvm/include/llvm/ObjectYAML/ELFYAML.h +++ b/llvm/include/llvm/ObjectYAML/ELFYAML.h @@ -211,6 +211,7 @@ struct Chunk { Dynamic, Group, RawContent, + CustomContent, Relocation, Relr, NoBits, @@ -398,6 +399,26 @@ struct RawContentSection : Section { std::optional> ContentBuf; }; +/// Abstract base class for non-blob contents. +struct CustomSection : Section { + std::optional Info; + + CustomSection() : Section(ChunkKind::CustomContent) {} + + /// Apply mappings. + virtual void sectionMapping(yaml::IO &IO) = 0; + + /// Decode Content and store to members. + virtual Error decode(const ArrayRef 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::CustomContent; + } +}; + struct NoBitsSection : Section { NoBitsSection() : Section(ChunkKind::NoBits) {} @@ -757,6 +778,52 @@ struct Object { bool shouldAllocateFileSpace(ArrayRef Phdrs, const NoBitsSection &S); +/// ELFYAML::Opt -- Null base class for ELFYAML to provide the +/// interface for handling CustomRawConetentSection. +/// +/// Users in ELFYAML should obtain the pointer with +/// dyn_cast if IO::Opt is the instance from yaml::Opt. +/// +/// if (auto *Opt = dyn_cast(IO.Opt)) +/// +/// Derivered classes should not modify OptClassID to ensue that +/// dyn_cast can find this interface. +class Opt : public yaml::IO::OptBase { +public: + Opt() { + OptBaseClassID = &ID; + OptClassID = &ID; + } + ~Opt(); + + /// Create an empty new object of CustomSection. + /// Its contents will be filled later. + /// This is called: + /// - Before preMapping for elf2yaml. + /// - After preMapping for yaml2elf. + /// Returns nullptr to delegate default actions. + virtual std::unique_ptr + makeCustomSection(StringRef Name) const; + + /// Called before mapping sections for prettyprinting yaml. + virtual void preMapping(const ELFYAML::Object &Object, bool IsOutputting); + + /// Called after mapping sections to gather members for the file format. + virtual void postMapping(const ELFYAML::Object &Object, bool IsOutputting); + + /// Tell IO::OptBase to be this and derivered classes. + static bool classof(const yaml::IO::OptBase *Obj) { + return (Obj->OptBaseClassID == &ID); + } + + /// This will be not needed unless the pointer to ELFYAML::Opt would + /// be cast further. + static bool classof(const Opt *Obj) { return (Obj->OptClassID == &ID); } + const char *OptClassID; + +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 1d04783753d5c..2b16dd21c56b6 100644 --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -962,8 +962,28 @@ class IO { } } +public: + /// The base class of options. + class OptBase { + public: + virtual ~OptBase(); + + static bool classof(const OptBase *Obj) { + return (Obj->OptBaseClassID == &ID); + } + const char *OptBaseClassID = &ID; + + private: + static const char ID; + }; + private: void *Ctxt; + OptBase DefaultOpt; + +public: + /// This may be overwritten in derivered classes. + OptBase *Opt = &DefaultOpt; }; namespace detail { diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index fc234581a45a7..f70f9e5601403 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -251,6 +251,9 @@ template class ELFState { void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::NoBitsSection &Section, ContiguousBlobAccumulator &CBA); + void writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::CustomSection &Section, + ContiguousBlobAccumulator &CBA); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, ContiguousBlobAccumulator &CBA); @@ -859,7 +862,9 @@ void ELFState::initSectionHeaders(std::vector &SHeaders, if (!isa(Sec) && (Sec->Content || Sec->Size)) SHeader.sh_size = writeContent(CBA, Sec->Content, Sec->Size); - if (auto S = dyn_cast(Sec)) { + if (auto S = dyn_cast(Sec)) { + writeSectionContent(SHeader, *S, CBA); + } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); } else if (auto S = dyn_cast(Sec)) { writeSectionContent(SHeader, *S, CBA); @@ -1264,6 +1269,22 @@ void ELFState::writeSectionContent( SHeader.sh_info = *Section.Info; } +template +void ELFState::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::CustomSection &Section, + ContiguousBlobAccumulator &CBA) { + if (Section.Info) + SHeader.sh_info = *Section.Info; + + // If Section has Content, emit it w/o encoding other fields. + if (Section.Content) + return; + + 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 89ffc383a4a6e..3f73637929cad 100644 --- a/llvm/lib/ObjectYAML/ELFYAML.cpp +++ b/llvm/lib/ObjectYAML/ELFYAML.cpp @@ -29,6 +29,22 @@ namespace llvm { ELFYAML::Chunk::~Chunk() = default; +ELFYAML::Opt::~Opt() = default; +const char ELFYAML::Opt::ID = 'E'; + +std::unique_ptr +ELFYAML::Opt::makeCustomSection(StringRef Name) const { + return nullptr; +} + +/// Called before mapping sections for prettyprinting yaml. +void ELFYAML::Opt::preMapping(const ELFYAML::Object &Object, + bool IsOutputting) {} + +/// Called after mapping sections to gather members for the file format. +void ELFYAML::Opt::postMapping(const ELFYAML::Object &Object, + bool IsOutputting) {} + namespace ELFYAML { ELF_ELFOSABI Object::getOSAbi() const { return Header.OSABI; } @@ -1582,6 +1598,20 @@ static bool isInteger(StringRef Val) { void MappingTraits>::mapping( IO &IO, std::unique_ptr &Section) { + if (!IO.outputting()) { + /// Prepare CustomSection by Name for ELFEmitter. + if (auto *Opt = dyn_cast(IO.Opt)) { + StringRef Name; + IO.mapOptional("Name", Name); + if (auto S = Opt->makeCustomSection(Name)) { + commonSectionMapping(IO, *S); + S->sectionMapping(IO); + Section = std::move(S); + return; + } + } + } + ELFYAML::ELF_SHT Type; StringRef TypeStr; if (IO.outputting()) { @@ -1731,7 +1761,10 @@ void MappingTraits>::mapping( Section = std::make_unique(); } - if (auto S = dyn_cast(Section.get())) + if (auto S = dyn_cast(Section.get())) { + commonSectionMapping(IO, *S); + S->sectionMapping(IO); + } else if (auto S = dyn_cast(Section.get())) sectionMapping(IO, *S); else sectionMapping(IO, *cast(Section.get())); @@ -1981,6 +2014,8 @@ void MappingTraits::mapping( void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { assert(!IO.getContext() && "The IO context is initialized already"); IO.setContext(&Object); + if (auto *Opt = dyn_cast(IO.Opt)) + Opt->preMapping(Object, IO.outputting()); IO.mapTag("!ELF", true); IO.mapRequired("FileHeader", Object.Header); IO.mapOptional("ProgramHeaders", Object.ProgramHeaders); @@ -1994,6 +2029,8 @@ void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { Object.DWARF->Is64BitAddrSize = Object.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64); } + if (auto *Opt = dyn_cast(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 56b557646100b..4b1c4b368d239 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -31,6 +31,14 @@ using namespace llvm; using namespace yaml; +//===----------------------------------------------------------------------===// +// IO::OptBase +//===----------------------------------------------------------------------===// + +IO::OptBase::~OptBase() = default; + +const char IO::OptBase::ID = '@'; + //===----------------------------------------------------------------------===// // IO //===----------------------------------------------------------------------===// diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index 9b4644bde36c0..8567de489a0bf 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -22,6 +22,7 @@ using namespace llvm; namespace { +struct DumperOpt : public ELFYAML::Opt {}; template class ELFDumper { @@ -80,6 +81,7 @@ class ELFDumper { Expected dumpRelrSection(const Elf_Shdr *Shdr); Expected dumpContentSection(const Elf_Shdr *Shdr); + Expected dumpCustomSection(const Elf_Shdr *Shdr); Expected dumpSymtabShndxSection(const Elf_Shdr *Shdr); Expected dumpNoBitsSection(const Elf_Shdr *Shdr); @@ -104,6 +106,8 @@ class ELFDumper { std::optional DWARF); public: + DumperOpt Opt; + ELFDumper(const object::ELFFile &O, std::unique_ptr DCtx); Expected dump(); }; @@ -657,6 +661,18 @@ ELFDumper::dumpSections() { if (!NameOrErr) return NameOrErr.takeError(); + if (auto ResultOrErr = dumpCustomSection(&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 +1670,38 @@ ELFDumper::dumpMipsABIFlags(const Elf_Shdr *Shdr) { return S.release(); } +template +Expected +ELFDumper::dumpCustomSection(const Elf_Shdr *Shdr) { + Expected NameOrErr = getUniquedSectionName(*Shdr); + if (Error E = NameOrErr.takeError()) + return nullptr; + auto Name = std::move(*NameOrErr); + + auto S = Opt.makeCustomSection(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 Content = *ContentOrErr; + if (Content.empty()) + return nullptr; + + if (Error E = S->decode(Content, Obj.isLE())) + return E; + return S.release(); +} + template static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj, std::unique_ptr DWARFCtx) { @@ -1664,6 +1712,7 @@ static Error elf2yaml(raw_ostream &Out, const object::ELFFile &Obj, std::unique_ptr 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 4a060e1aad427..b899b8d7cdf2c 100644 --- a/llvm/tools/yaml2obj/yaml2obj.cpp +++ b/llvm/tools/yaml2obj/yaml2obj.cpp @@ -58,6 +58,8 @@ static cl::opt MaxSize( cl::opt OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"), cl::Prefix, cl::cat(Cat)); + +struct EmitterOpt : public ELFYAML::Opt {}; } // namespace static std::optional preprocess(StringRef Buf, @@ -142,7 +144,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))