diff --git a/include/eld/Config/GeneralOptions.h b/include/eld/Config/GeneralOptions.h index 66dcee806..ac14c81e5 100644 --- a/include/eld/Config/GeneralOptions.h +++ b/include/eld/Config/GeneralOptions.h @@ -485,6 +485,15 @@ class GeneralOptions { bool printGCSections() const { return BPrintGCSections; } + // --plugin-activity-file + void setPluginActivityLogFile(llvm::StringRef File) { + PluginActivityLogFile = File.str(); + } + + const std::optional &getPluginActivityLogFile() const { + return PluginActivityLogFile; + } + // --ld-generated-unwind-info void setGenUnwindInfo(bool PEnable = true) { BGenUnwindInfo = PEnable; } @@ -1267,6 +1276,7 @@ class GeneralOptions { std::string MapFile; // Mapfile std::string TarFile; // --reproduce output tarfile name std::string TimingStatsFile; + std::optional PluginActivityLogFile; // --plugin-activity-file output path std::string MappingFileName; // --Mapping-file std::string MappingDumpFile; // --dump-mapping-file std::string ResponseDumpFile; // --dump-response-file diff --git a/include/eld/Core/LinkState.h b/include/eld/Core/LinkState.h new file mode 100644 index 000000000..7e1bf0396 --- /dev/null +++ b/include/eld/Core/LinkState.h @@ -0,0 +1,55 @@ +//===- LinkState.h--------------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// +#ifndef ELD_CORE_LINKSTATE_H +#define ELD_CORE_LINKSTATE_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +namespace eld { +/// Stages of the link process. +/// What actions a plugin can perform depends on the +/// link state. For example: +/// - Plugins can change the output section of an input +/// section only in BeforeLayout link state. +/// - Plugins can move chunks from one output section to +/// another only in CreatingSections link state. +/// - Plugins can only compute the output image layout checkum using +/// `LinkerWrapper::getImageLayoutChecksum` only in AfterLayout linker +/// state. +/// - Plugins can reassign virtual addresses using +/// `LinkerWrapper::reassignVirtualAddresses()` only in CreatingSegments +/// link state. +/// +/// Plugin authors should ensure that the action being performed by the +/// plugin is meaningful in the link state in which it is executed. +/// Executing invalid actions for a link state can result in undefined +/// behavior. +enum LinkState : uint8_t { + Unknown, + Initializing, + BeforeLayout, + CreatingSections, + CreatingSegments, + AfterLayout, +}; + +static inline llvm::StringRef getLinkStateStrRef(LinkState State) { +#define ADD_CASE(S) \ + case LinkState::S: \ + return #S; + switch (State) { + ADD_CASE(Unknown) + ADD_CASE(Initializing) + ADD_CASE(BeforeLayout) + ADD_CASE(CreatingSections) + ADD_CASE(CreatingSegments) + ADD_CASE(AfterLayout) + } +#undef ADD_CASE + llvm_unreachable("Invalid LinkState"); +} +} // namespace eld +#endif \ No newline at end of file diff --git a/include/eld/Core/Module.h b/include/eld/Core/Module.h index d7ee3ae36..6dec41e70 100644 --- a/include/eld/Core/Module.h +++ b/include/eld/Core/Module.h @@ -19,8 +19,10 @@ #include "eld/Config/GeneralOptions.h" #include "eld/Core/Linker.h" +#include "eld/Core/LinkState.h" #include "eld/Input/InputFile.h" #include "eld/LayoutMap/LayoutInfo.h" +#include "eld/Plugin/PluginActivityLog.h" #include "eld/Plugin/PluginManager.h" #include "eld/PluginAPI/LinkerWrapper.h" #include "eld/Script/StrToken.h" @@ -141,33 +143,6 @@ class Module { typedef std::pair DynamicListStartEndIndexPair; - /// Stages of the link process. - /// What actions a plugin can perform depends on the - /// link state. For example: - /// - Plugins can change the output section of an input - /// section only in BeforeLayout link state. - /// - Plugins can move chunks from one output section to - /// another only in CreatingSections link state. - /// - Plugins can only compute the output image layout checkum using - /// `LinkerWrapper::getImageLayoutChecksum` only in AfterLayout linker - /// state. - /// - Plugins can reassign virtual addresses using - /// `LinkerWrapper::reassignVirtualAddresses()` only in CreatingSegments - /// link state. - /// - /// Plugin authors should ensure that the action being performed by the - /// plugin is meaningful in the link state in which it is executed. - /// Executing invalid actions for a link state can result in undefined - /// behavior. - enum LinkState : uint8_t { - Unknown, - Initializing, - BeforeLayout, - CreatingSections, - CreatingSegments, - AfterLayout, - }; - public: explicit Module(LinkerScript &CurScript, LinkerConfig &Config, LayoutInfo *LayoutInfo); @@ -638,19 +613,19 @@ class Module { } bool isLinkStateBeforeLayout() const { - return getState() == Module::LinkState::BeforeLayout; + return getState() == LinkState::BeforeLayout; } bool isLinkStateCreatingSections() const { - return getState() == Module::LinkState::CreatingSections; + return getState() == LinkState::CreatingSections; } bool isLinkStateCreatingSegments() const { - return getState() == Module::LinkState::CreatingSegments; + return getState() == LinkState::CreatingSegments; } bool isLinkStateAfterLayout() const { - return getState() == Module::LinkState::AfterLayout; + return getState() == LinkState::AfterLayout; } void setFragmentPaddingValue(Fragment *F, uint64_t V); @@ -664,6 +639,14 @@ class Module { bool isBackendInitialized() const; + void createPluginActivityLog() { + PluginActLog.emplace(ThisConfig.options(), UserLinkerScript.getPlugins()); + } + + std::optional &getPluginActivityLog() { + return PluginActLog; + } + private: /// Verifies invariants of 'CreatingSections' linker state. /// Invariants here means the conditions and rules that 'CreatingSections' @@ -744,6 +727,8 @@ class Module { llvm::DenseMap FragmentPaddingValues; PluginManager PM; NamePool SymbolNamePool; + + std::optional PluginActLog; }; } // namespace eld diff --git a/include/eld/Driver/GnuLinkerOptions.td b/include/eld/Driver/GnuLinkerOptions.td index fdbc0d209..ff4fe25dd 100644 --- a/include/eld/Driver/GnuLinkerOptions.td +++ b/include/eld/Driver/GnuLinkerOptions.td @@ -1173,6 +1173,12 @@ def noDefaultPlugins HelpText<"Allow no plugins to be implicitly loaded">, Group; +defm PluginActivityFile + : smDashWithOpt<"plugin-activity-file", "PluginActivityFile", + "Emit plugin activity JSON files">, + MetaVarName<"">, + Group; + //===----------------------------------------------------------------------===// /// Other Features //===----------------------------------------------------------------------===// diff --git a/include/eld/Fragment/Fragment.h b/include/eld/Fragment/Fragment.h index b9601bc01..52cad2126 100644 --- a/include/eld/Fragment/Fragment.h +++ b/include/eld/Fragment/Fragment.h @@ -22,6 +22,7 @@ namespace eld { class DiagnosticEngine; class ELFSection; +class GeneralOptions; class LinkerConfig; class Module; class ResolveInfo; @@ -127,6 +128,11 @@ class Fragment { bool originatesFromPlugin(const Module &Module) const; + /// Returns a string containing basic fragment information, such as, section + /// and input file name. It is intended to be used for printing to log files, + /// diagnostics, or for quick debugging. + std::string str(const GeneralOptions &Options) const; + private: Fragment(const Fragment &); // DO NOT IMPLEMENT Fragment &operator=(const Fragment &); // DO NOT IMPLEMENT diff --git a/include/eld/Plugin/PluginActivityLog.h b/include/eld/Plugin/PluginActivityLog.h new file mode 100644 index 000000000..7d87bf768 --- /dev/null +++ b/include/eld/Plugin/PluginActivityLog.h @@ -0,0 +1,83 @@ +//===- PluginData.h--------------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// +#ifndef ELD_PLUGIN_PLUGINACTIVITYLOG_H +#define ELD_PLUGIN_PLUGINACTIVITYLOG_H +#include "eld/Plugin/PluginOp.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" +#include +#include +#include + +namespace eld { +class GeneralOptions; +class Plugin; + +/// Records ordered plugin operations. +class PluginActivityLog { +public: + PluginActivityLog(const GeneralOptions &GO, + const std::vector &pAllPlugins) + : Options(GO), AllPlugins(pAllPlugins) {} + + void addPluginOperation(PluginOp &Op) { + PluginOperations.push_back(std::ref(Op)); + } + + /// Emit ordered plugin operations to a JSON file for analysis. + bool print(llvm::StringRef Filename) const; + + void recordPluginLibrary(void *Handle, std::string Path) { + PluginLibraryHandleToPathMap[Handle] = Path; + } + + std::string getPluginLibraryPath(void *LibHandle) const { + auto It = PluginLibraryHandleToPathMap.find(LibHandle); + if (It != PluginLibraryHandleToPathMap.end()) + return It->second; + return ""; + } + +private: + // Convert a PluginOp to JSON based on its dynamic type. + llvm::json::Object toJSON(const PluginOp &Op) const; + + // Overloads for each concrete PluginOp to extract a minimal JSON payload. + llvm::json::Object toJSON(const ChangeOutputSectionPluginOp &P) const; + + llvm::json::Object toJSON(const AddChunkPluginOp &P) const; + + llvm::json::Object toJSON(const RemoveChunkPluginOp &P) const; + + llvm::json::Object toJSON(const UpdateChunksPluginOp &P) const; + + llvm::json::Object toJSON(const RemoveSymbolPluginOp &P) const; + + llvm::json::Object toJSON(const RelocationDataPluginOp &P) const; + + llvm::json::Object toJSON(const UpdateLinkStatsPluginOp &P) const; + + llvm::json::Object toJSON(const UpdateRulePluginOp &P) const; + + llvm::json::Object toJSON(const ResetOffsetPluginOp &P) const; + + llvm::json::Object toJSON(const UpdateLinkStateOp &P) const; + + llvm::json::Object getBaseActivityJSONObject(const PluginOp &Op) const; + + llvm::json::Array getPluginActivities() const; + + llvm::json::Array getPluginsInfo() const; + +private: + std::vector> PluginOperations; + const GeneralOptions &Options; + const std::vector &AllPlugins; + std::unordered_map PluginLibraryHandleToPathMap; +}; +} // namespace eld + +#endif diff --git a/include/eld/Plugin/PluginOp.h b/include/eld/Plugin/PluginOp.h index 384497083..74ae00cc5 100644 --- a/include/eld/Plugin/PluginOp.h +++ b/include/eld/Plugin/PluginOp.h @@ -7,6 +7,8 @@ #ifndef ELD_PLUGIN_PLUGINOP_H #define ELD_PLUGIN_PLUGINOP_H +#include "eld/Core/LinkState.h" +#include "llvm/ADT/StringRef.h" #include #include @@ -34,16 +36,39 @@ class PluginOp { UpdateLinkStat, UpdateRule, RelocationData, + UpdateLinkState }; explicit PluginOp(plugin::LinkerWrapper *, PluginOpType T, std::string Annotation); + explicit PluginOp(PluginOpType T) : OpType(T) {} + PluginOpType getPluginOpType() const { return OpType; } std::string getAnnotation() const { return Annotation; } - std::string getPluginName() const; + virtual std::string getPluginName() const; + + llvm::StringRef getPluginOpTypeStrRef() const { +#define ADD_CASE(C) \ + case C: \ + return #C; + switch (OpType) { + ADD_CASE(ChangeOutputSection) + ADD_CASE(AddChunk) + ADD_CASE(RemoveChunk) + ADD_CASE(RemoveSymbol) + ADD_CASE(ResetOffset) + ADD_CASE(UpdateChunks) + ADD_CASE(UpdateLinkStat) + ADD_CASE(UpdateRule) + ADD_CASE(RelocationData) + ADD_CASE(UpdateLinkState) + } +#undef ADD_CASE + return "Unknown"; + } virtual std::string getPluginOpStr() const { return ""; } @@ -143,6 +168,8 @@ class UpdateChunksPluginOp : public PluginOp { RuleContainer *getRule() const { return Rule; } + Type getType() const { return T; } + private: RuleContainer *Rule = nullptr; Type T = Start; @@ -192,6 +219,10 @@ class UpdateLinkStatsPluginOp : public PluginOp { return P->getPluginOpType() == PluginOpType::UpdateLinkStat; } + llvm::StringRef getStatName() const { + return StatName; + } + private: std::string StatName; }; @@ -234,6 +265,24 @@ class ResetOffsetPluginOp : public PluginOp { uint32_t OldOffset; }; +// Pseudo plugin operation because it is not actually +// performed by a plugin, but is instead performed +// by the linker. +class UpdateLinkStateOp : public PluginOp { +public: + UpdateLinkStateOp(LinkState pNewState) + : PluginOp(PluginOpType::UpdateLinkState), NewState(pNewState) {} + + LinkState getNewState() const { return NewState; } + + std::string getPluginName() const override { + return "Linker"; + } + +private: + LinkState NewState = LinkState::Unknown; +}; + } // namespace eld #endif diff --git a/lib/Core/Linker.cpp b/lib/Core/Linker.cpp index bbaf23828..64e57661c 100644 --- a/lib/Core/Linker.cpp +++ b/lib/Core/Linker.cpp @@ -675,7 +675,7 @@ bool Linker::layout() { eld::RegisterTimer T("AfterLayout OutputSection Iterator", "Perform Layout", ThisConfig->options().printTimingStats("plugin")); // Run the output section iterator plugin after all the layout is done. - ThisModule->setLinkState(Module::LinkState::AfterLayout); + ThisModule->setLinkState(LinkState::AfterLayout); Backend->finalizeLayout(); { diff --git a/lib/Core/LinkerScript.cpp b/lib/Core/LinkerScript.cpp index 42e3b9599..a63dc232e 100644 --- a/lib/Core/LinkerScript.cpp +++ b/lib/Core/LinkerScript.cpp @@ -190,12 +190,16 @@ void LinkerScript::addSectionOverride(plugin::LinkerWrapper *W, eld::Module *M, if (!layoutInfo) return; layoutInfo->recordSectionOverride(W, Op); + if (auto &PluginActLog = M->getPluginActivityLog()) + PluginActLog->addPluginOperation(*Op); } void LinkerScript::removeSymbolOp(plugin::LinkerWrapper *W, eld::Module *M, const ResolveInfo *S) { RemoveSymbolPluginOp *Op = make(W, "", S); LayoutInfo *layoutInfo = M->getLayoutInfo(); + if (auto &PluginActLog = M->getPluginActivityLog()) + PluginActLog->addPluginOperation(*Op); if (!layoutInfo) return; layoutInfo->recordRemoveSymbol(W, Op); @@ -252,10 +256,14 @@ eld::Expected LinkerScript::addChunkOp(plugin::LinkerWrapper *W, W->getPlugin()->recordFragmentAdd(R, F); + if (auto &PluginActLog = M->getPluginActivityLog()) + PluginActLog->addPluginOperation(*Op); + LayoutInfo *layoutInfo = M->getLayoutInfo(); if (!layoutInfo) return {}; layoutInfo->recordAddChunk(W, Op); + // FIXME: This verbose diagnostic is not printed if layoutInfo is null! if (M->getPrinter()->isVerbose()) Diag->raise(Diag::added_chunk_op) << R->getSection()->getOutputSection()->name() << Annotation; @@ -284,10 +292,13 @@ eld::Expected LinkerScript::removeChunkOp(plugin::LinkerWrapper *W, R->getAsString(), F->getOutputELFSection()->getDecoratedName(Config.options())}); + if (auto &PluginActLog = M->getPluginActivityLog()) + PluginActLog->addPluginOperation(*Op); LayoutInfo *layoutInfo = M->getLayoutInfo(); if (!layoutInfo) return {}; layoutInfo->recordRemoveChunk(W, Op); + // FIXME: This verbose diagnostic is not printed if layoutInfo is null! if (M->getPrinter()->isVerbose()) Diag->raise(Diag::removed_chunk_op) << R->getSection()->name() << Annotation; @@ -298,19 +309,26 @@ eld::Expected LinkerScript::updateChunksOp( plugin::LinkerWrapper *W, eld::Module *M, RuleContainer *R, std::vector &Frags, std::string Annotation) { LayoutInfo *layoutInfo = M->getLayoutInfo(); - if (layoutInfo) { + auto &PluginActLog = M->getPluginActivityLog(); + if (layoutInfo || PluginActLog) { UpdateChunksPluginOp *Op = eld::make( W, R, UpdateChunksPluginOp::Type::Start, Annotation); - layoutInfo->recordUpdateChunks(W, Op); + if (layoutInfo) + layoutInfo->recordUpdateChunks(W, Op); + if (PluginActLog) + PluginActLog->addPluginOperation(*Op); } llvm::SmallVectorImpl &FragmentsInRule = R->getSection()->getFragmentList(); - if (layoutInfo) { + if (layoutInfo || PluginActLog) { for (auto &Frag : FragmentsInRule) { RemoveChunkPluginOp *Op = eld::make(W, R, Frag, Annotation); - layoutInfo->recordRemoveChunk(W, Op); + if (layoutInfo) + layoutInfo->recordRemoveChunk(W, Op); + if (PluginActLog) + PluginActLog->addPluginOperation(*Op); } } @@ -326,10 +344,13 @@ eld::Expected LinkerScript::updateChunksOp( ELDEXP_RETURN_DIAGENTRY_IF_ERROR(ExpAddChunk); } - if (layoutInfo) { + if (layoutInfo || PluginActLog) { UpdateChunksPluginOp *Op = eld::make( W, R, UpdateChunksPluginOp::Type::End, Annotation); - layoutInfo->recordUpdateChunks(W, Op); + if (layoutInfo) + layoutInfo->recordUpdateChunks(W, Op); + if (PluginActLog) + PluginActLog->addPluginOperation(*Op); } return {}; } @@ -478,11 +499,14 @@ bool LinkerScript::loadPlugin(Plugin &P, Module &M) { addPluginToTar(P.getName(), ResolvedPath, M.getOutputTarWriter()); auto I = MLibraryToPluginMap.find(ResolvedPath); void *Handle = nullptr; + auto &PAL = M.getPluginActivityLog(); if (I == MLibraryToPluginMap.end()) { Handle = Plugin::loadPlugin(ResolvedPath, &M); MLibraryToPluginMap.insert(std::make_pair(ResolvedPath, &P)); } else { Handle = I->second->getLibraryHandle(); + if (PAL) + PAL->recordPluginLibrary(Handle, ResolvedPath); } if (!Handle) return false; diff --git a/lib/Core/Module.cpp b/lib/Core/Module.cpp index c0a9a309e..80858ddc7 100644 --- a/lib/Core/Module.cpp +++ b/lib/Core/Module.cpp @@ -57,6 +57,8 @@ Module::Module(LinkerScript &CurScript, LinkerConfig &Config, Printer = ThisConfig.getPrinter(); if (ThisConfig.shouldCreateReproduceTar()) createOutputTarWriter(); + if (Config.options().getPluginActivityLogFile()) + createPluginActivityLog(); } Module::Module(const std::string &Name, LinkerScript &CurScript, @@ -70,6 +72,8 @@ Module::Module(const std::string &Name, LinkerScript &CurScript, UserLinkerScript.setHashingEnabled(); UserLinkerScript.createSectionMap(CurScript, Config, LayoutInfo); Printer = ThisConfig.getPrinter(); + if (Config.options().getPluginActivityLogFile()) + createPluginActivityLog(); } Module::~Module() { @@ -505,20 +509,7 @@ bool Module::updateOutputSectionsWithPlugins() { } llvm::StringRef Module::getStateStr() const { - switch (getState()) { - case LinkState::Unknown: - return "Unknown"; - case LinkState::Initializing: - return "Initializing"; - case LinkState::BeforeLayout: - return "BeforeLayout"; - case LinkState::CreatingSections: - return "CreatingSections"; - case LinkState::AfterLayout: - return "AfterLayout"; - case LinkState::CreatingSegments: - return "CreatingSegments"; - } + return getLinkStateStrRef(getState()); } void Module::addSymbolCreatedByPluginToFragment(Fragment *F, std::string Symbol, @@ -866,6 +857,11 @@ bool Module::setLinkState(LinkState S) { if (S == LinkState::CreatingSections) Verification = verifyInvariantsForCreatingSectionsState(); State = S; + auto &PluginActLog = getPluginActivityLog(); + if (PluginActLog) { + auto LinkStateOp = eld::make(S); + PluginActLog->addPluginOperation(*LinkStateOp); + } return Verification; } diff --git a/lib/Fragment/Fragment.cpp b/lib/Fragment/Fragment.cpp index 668c77ebd..ade9bcd04 100644 --- a/lib/Fragment/Fragment.cpp +++ b/lib/Fragment/Fragment.cpp @@ -150,3 +150,12 @@ bool Fragment::originatesFromPlugin(const Module &Module) const { return getOwningSection()->getInputFile() == Module.getInternalInput(Module::InternalInputType::Plugin); } + +std::string Fragment::str(const GeneralOptions &Options) const { + ELFSection *S = getOwningSection(); + if (!S) + return ""; + InputFile *IF = S->originalInput(); + ASSERT(IF, "Input section must have an InputFile!"); + return IF->getInput()->decoratedPath() + "(" + S->getDecoratedName(Options) + ")"; +} \ No newline at end of file diff --git a/lib/LinkerWrapper/GnuLdDriver.cpp b/lib/LinkerWrapper/GnuLdDriver.cpp index 480ceb6a5..68823daba 100644 --- a/lib/LinkerWrapper/GnuLdDriver.cpp +++ b/lib/LinkerWrapper/GnuLdDriver.cpp @@ -1186,6 +1186,11 @@ bool GnuLdDriver::processOptions(llvm::opt::InputArgList &Args) { Config.options().setOMagic(true); } + // --plugin-activity-file= + if (llvm::opt::Arg *A = Args.getLastArg(T::PluginActivityFile)) { + Config.options().setPluginActivityLogFile(A->getValue()); + } + Config.options().setUnknownOptions(Args.getAllArgValues(T::UNKNOWN)); return true; } @@ -1739,6 +1744,9 @@ bool GnuLdDriver::doLink(llvm::opt::InputArgList &Args, if (!linkStatus || Config.options().getRecordInputFiles()) handleReproduce(Args, actions, true); linker.printLayout(); + if (auto &PluginActLog = ThisModule->getPluginActivityLog()) { + PluginActLog->print(Config.options().getPluginActivityLogFile().value()); + } linkStatus &= ThisModule->getPluginManager().callDestroyHook(); // llvm::errs() << "destroy hook: linkStatus: " << linkStatus << "\n"; linker.unloadPlugins(); diff --git a/lib/LinkerWrapper/LinkerWrapper.cpp b/lib/LinkerWrapper/LinkerWrapper.cpp index 4bfeb69c3..c872d4ea1 100644 --- a/lib/LinkerWrapper/LinkerWrapper.cpp +++ b/lib/LinkerWrapper/LinkerWrapper.cpp @@ -960,7 +960,7 @@ bool LinkerWrapper::isChunkMovableFromOutputSection(const Chunk &C) const { std::string_view LinkerWrapper::getCurrentLinkStateAsStr() const { switch (getLinkState()) { #define ADD_CASE(linkerState) \ - case Module::LinkState::linkerState: \ + case LinkState::linkerState: \ return #linkerState; ADD_CASE(Unknown); ADD_CASE(Initializing); @@ -1168,21 +1168,21 @@ eld::Expected LinkerWrapper::doesRuleMatchWithSection( uint8_t LinkerWrapper::getLinkState() const { return m_Module.getState(); } bool LinkerWrapper::isLinkStateInitializing() const { - return m_Module.getState() == Module::LinkState::Initializing; + return m_Module.getState() == LinkState::Initializing; } bool LinkerWrapper::isLinkStateBeforeLayout() const { - return m_Module.getState() == Module::LinkState::BeforeLayout; + return m_Module.getState() == LinkState::BeforeLayout; } bool LinkerWrapper::isLinkStateCreatingSections() const { - return m_Module.getState() == Module::LinkState::CreatingSections; + return m_Module.getState() == LinkState::CreatingSections; } bool LinkerWrapper::isLinkStateAfterLayout() const { - return m_Module.getState() == Module::LinkState::AfterLayout; + return m_Module.getState() == LinkState::AfterLayout; } bool LinkerWrapper::isLinkStateCreatingSegments() const { - return m_Module.getState() == Module::LinkState::CreatingSegments; + return m_Module.getState() == LinkState::CreatingSegments; } diff --git a/lib/Object/ObjectBuilder.cpp b/lib/Object/ObjectBuilder.cpp index 67a8004b4..db4b41976 100644 --- a/lib/Object/ObjectBuilder.cpp +++ b/lib/Object/ObjectBuilder.cpp @@ -470,7 +470,7 @@ void ObjectBuilder::assignOutputSections(std::vector Inputs, bool HasSectionsCommand = ThisModule.getScript().linkerScriptHasSectionsCommand(); - ThisModule.setLinkState(Module::LinkState::BeforeLayout); + ThisModule.setLinkState(LinkState::BeforeLayout); std::sort(Inputs.begin(), Inputs.end(), [](InputFile *A, InputFile *B) { return A->getNumSections() > B->getNumSections(); diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp index 839858cf4..86ed14741 100644 --- a/lib/Object/ObjectLinker.cpp +++ b/lib/Object/ObjectLinker.cpp @@ -1231,7 +1231,7 @@ bool ObjectLinker::mergeSections() { OutBegin = ThisModule->getScript().sectionMap().begin(); OutEnd = ThisModule->getScript().sectionMap().end(); - ThisModule->setLinkState(Module::LinkState::CreatingSections); + ThisModule->setLinkState(LinkState::CreatingSections); if (!ThisConfig.getDiagEngine()->diagnose()) return false; diff --git a/lib/Plugin/CMakeLists.txt b/lib/Plugin/CMakeLists.txt index 8aac60810..aa4fb93ab 100644 --- a/lib/Plugin/CMakeLists.txt +++ b/lib/Plugin/CMakeLists.txt @@ -1,2 +1,2 @@ -llvm_add_library(ELDPluginOp STATIC PluginData.cpp PluginManager.cpp - PluginOp.cpp) +llvm_add_library(ELDPluginOp STATIC PluginActivityLog.cpp PluginData.cpp + PluginManager.cpp PluginOp.cpp) diff --git a/lib/Plugin/PluginActivityLog.cpp b/lib/Plugin/PluginActivityLog.cpp new file mode 100644 index 000000000..f1dc02388 --- /dev/null +++ b/lib/Plugin/PluginActivityLog.cpp @@ -0,0 +1,180 @@ +// PluginActivityLog.cpp------------------------------------------------------===// +// Part of the eld Project, under the BSD License +// See https://github.com/qualcomm/eld/LICENSE.txt for license information. +// SPDX-License-Identifier: BSD-3-Clause +//===-------------------------------------------------------------------------===// +#include "eld/Plugin/PluginActivityLog.h" +#include "eld/Config/Version.h" +#include "eld/Core/LinkState.h" +#include "eld/Core/LinkerScript.h" +#include "eld/Fragment/Fragment.h" +#include "eld/Object/RuleContainer.h" +#include "eld/Plugin/PluginOp.h" +#include "eld/Readers/ELFSection.h" +#include "eld/Script/Plugin.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/JSON.h" + +using namespace eld; + +bool PluginActivityLog::print(llvm::StringRef Filename) const { + std::error_code EC; + llvm::raw_fd_ostream OS(Filename, EC, llvm::sys::fs::OF_None); + if (EC) + return false; + llvm::json::Array Ops = getPluginActivities(); + llvm::json::Array PluginsInfo = getPluginsInfo(); + llvm::json::Object Root{{"PluginActivities", std::move(Ops)}}; + Root["LinkerRevision"] = getELDRevision(); + Root["Plugins"] = std::move(PluginsInfo); + OS << llvm::formatv("{0:2}\n", llvm::json::Value(std::move(Root))); + return true; +} + +llvm::json::Object PluginActivityLog::toJSON(const PluginOp &Op) const { + switch (Op.getPluginOpType()) { + case PluginOp::ChangeOutputSection: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::AddChunk: + return PluginActivityLog::toJSON(static_cast(Op)); + case PluginOp::RemoveChunk: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::UpdateChunks: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::RemoveSymbol: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::RelocationData: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::UpdateLinkStat: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::UpdateRule: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::ResetOffset: + return PluginActivityLog::toJSON( + static_cast(Op)); + case PluginOp::UpdateLinkState: + return PluginActivityLog::toJSON( + static_cast(Op)); + default: + return llvm::json::Object{{"type", "Unknown"}}; + } +} + +// Overloads for each concrete PluginOp to extract a minimal JSON payload. +llvm::json::Object +PluginActivityLog::toJSON(const ChangeOutputSectionPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + return JSONObj; +} + +llvm::json::Object PluginActivityLog::toJSON(const AddChunkPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Rule"] = Op.getRule()->getAsString(); + JSONObj["Fragment"] = Op.getFrag()->str(Options); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const RemoveChunkPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Rule"] = Op.getRule()->getAsString(); + JSONObj["Fragment"] = Op.getFrag()->str(Options); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const UpdateChunksPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Info"] = + (Op.getType() == UpdateChunksPluginOp::Type::Start ? "Start" : "End"); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const RemoveSymbolPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Symbol"] = Op.getRemovedSymbol()->getName(); + return JSONObj; +} + +// FIXME: This needs to be completed. +llvm::json::Object +PluginActivityLog::toJSON(const RelocationDataPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const UpdateLinkStatsPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Stat"] = Op.getStatName(); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const UpdateRulePluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["Rule"] = Op.getRule()->getAsString(); + JSONObj["Section"] = Op.getSection()->getDecoratedName(Options); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const ResetOffsetPluginOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["OutputSection"] = Op.getOutputSection()->name(); + JSONObj["OldOffset"] = Op.getOldOffset(); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::toJSON(const UpdateLinkStateOp &Op) const { + auto JSONObj = getBaseActivityJSONObject(Op); + JSONObj["NewState"] = getLinkStateStrRef(Op.getNewState()); + return JSONObj; +} + +llvm::json::Object +PluginActivityLog::getBaseActivityJSONObject(const PluginOp &Op) const { + return llvm::json::Object{{"Plugin", Op.getPluginName()}, + {"Type", Op.getPluginOpTypeStrRef()}, + {"Annotation", Op.getAnnotation()}}; +} + +llvm::json::Array PluginActivityLog::getPluginActivities() const { + llvm::json::Array Ops; + for (const auto &OpRef : PluginOperations) { + const PluginOp &Op = OpRef.get(); + Ops.push_back(toJSON(Op)); + } + return Ops; +} + +llvm::json::Array PluginActivityLog::getPluginsInfo() const { + llvm::json::Array PluginsInfo; + for (const auto *P : AllPlugins) { + llvm::errs() << "P->getPluginName(): " << P->getPluginName() << "\n"; + llvm::json::Object PInfo; + PInfo["Name"] = P->getPluginName(); + PInfo["Autloaded"] = P->isDefaultPlugin(); + PInfo["Library"] = getPluginLibraryPath(P->getLibraryHandle()); + PInfo["RegisteredCommandLineOptions"] = llvm::json::Array(); + for (const auto &Opt : P->getPluginCommandLineOptions()) { + llvm::json::Object OptInfo; + OptInfo["Option"] = Opt.Option; + OptInfo["HasValue"] = Opt.HasValue; + auto &PluginCommandLineOptionsInfo = + *PInfo.getArray("RegisteredCommandLineOptions"); + PluginCommandLineOptionsInfo.push_back(std::move(OptInfo)); + } + PluginsInfo.push_back(std::move(PInfo)); + } + return PluginsInfo; +} diff --git a/lib/Target/GNULDBackend.cpp b/lib/Target/GNULDBackend.cpp index 13358b2a9..9bab7995f 100644 --- a/lib/Target/GNULDBackend.cpp +++ b/lib/Target/GNULDBackend.cpp @@ -4618,7 +4618,7 @@ void GNULDBackend::resolveTargetDefinedSymbols() { void GNULDBackend::doPostLayout() { resolveTargetDefinedSymbols(); - m_Module.setLinkState(Module::LinkState::CreatingSegments); + m_Module.setLinkState(LinkState::CreatingSegments); if (!m_Module.getLinker() ->getObjectLinker() ->runOutputSectionIteratorPlugin()) { diff --git a/test/Common/Plugin/CMakeLists.txt b/test/Common/Plugin/CMakeLists.txt index 0b6cc5f84..43d22f4b0 100644 --- a/test/Common/Plugin/CMakeLists.txt +++ b/test/Common/Plugin/CMakeLists.txt @@ -52,6 +52,7 @@ add_subdirectory(PartialPendingSectionOverrides) add_subdirectory(PendingAndNoSectionOverrides) add_subdirectory(PendingRuleInsertions) add_subdirectory(PendingSectionOverrides) +add_subdirectory(PluginActivityLogFile) add_subdirectory(PluginDiagCrash) add_subdirectory(PluginDiagnostics) add_subdirectory(ActBeforePerformingLayout) diff --git a/test/Common/Plugin/PluginActivityLogFile/CMakeLists.txt b/test/Common/Plugin/PluginActivityLogFile/CMakeLists.txt new file mode 100644 index 000000000..4b3cfd830 --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/CMakeLists.txt @@ -0,0 +1,20 @@ +set(SOURCES PluginActivityLogPlugin.cpp) + +if(NOT CYGWIN AND LLVM_ENABLE_PIC) + set(SHARED_LIB_SOURCES ${SOURCES}) + + set(bsl ${BUILD_SHARED_LIBS}) + + set(BUILD_SHARED_LIBS ON) + + add_llvm_library(PluginActivityLogPlugin ${SHARED_LIB_SOURCES} LINK_LIBS LW) + + set_target_properties( + PluginActivityLogPlugin + PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/test") + + set(BUILD_SHARED_LIBS ${bsl}) +endif() + +add_plugin(PluginActivityLogPlugin) diff --git a/test/Common/Plugin/PluginActivityLogFile/Inputs/1.c b/test/Common/Plugin/PluginActivityLogFile/Inputs/1.c new file mode 100644 index 000000000..57984df48 --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/Inputs/1.c @@ -0,0 +1,7 @@ +int foo1() { return 1; } +int foo2() { return 3; } + +int bar1() { return 1; } +int bar2() { return 3; } + +int baz() { return 5; } \ No newline at end of file diff --git a/test/Common/Plugin/PluginActivityLogFile/Inputs/PluginConfig.yaml b/test/Common/Plugin/PluginActivityLogFile/Inputs/PluginConfig.yaml new file mode 100644 index 000000000..e49bc516b --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/Inputs/PluginConfig.yaml @@ -0,0 +1,7 @@ +GlobalPlugins: + - Type: OutputSectionIterator + Name: OutSectIterPluginActLog + Library: PluginActivityLogPlugin + - Type: LinkerPlugin + Name: LPPluginActLog + Library: PluginActivityLogPlugin \ No newline at end of file diff --git a/test/Common/Plugin/PluginActivityLogFile/Inputs/script.t b/test/Common/Plugin/PluginActivityLogFile/Inputs/script.t new file mode 100644 index 000000000..abc5e9019 --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/Inputs/script.t @@ -0,0 +1,4 @@ +SECTIONS { + .foo : { *(.text.foo*) } + .bar : { *(.text.bar*) } +} diff --git a/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogFile.test b/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogFile.test new file mode 100644 index 000000000..7adba31c0 --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogFile.test @@ -0,0 +1,153 @@ +#---PluginActivityLogFile.test---------------- Executable,LS ----------------# +#BEGIN_COMMENT +# Exercises PluginActivityLog by running a simple LinkerPlugin that triggers +# plugin operations. Verifies the JSON log file is emitted when +# --plugin-activity-file is passed. +#END_COMMENT + +#START_TEST +RUN: %clang %clangopts -o %t1.1.o %p/Inputs/1.c -c -ffunction-sections +RUN: %link %linkopts -o %t1.1.out %t1.1.o -L %libsdir/test \ +RUN: --plugin-config=%p/Inputs/PluginConfig.yaml \ +RUN: -T %p/Inputs/script.t \ +RUN: --plugin-activity-file=%t1.1.plugin.map.json 2>&1 | %filecheck %s +RUN: %filecheck %s --check-prefix PLUGIN_ACT < %t1.1.plugin.map.json +RUN: %filecheck %s --check-prefix PLUGINS_INFO < %t1.1.plugin.map.json +#END_TEST + +CHECK: Note: Stripping symbol baz + +PLUGIN_ACT: "LinkerRevision": "{{.+}}", +PLUGIN_ACT: "PluginActivities": [ +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "NewState": "BeforeLayout", +PLUGIN_ACT-DAG: "Plugin": "Linker", +PLUGIN_ACT-DAG: "Type": "UpdateLinkState" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "NewState": "CreatingSections", +PLUGIN_ACT-DAG: "Plugin": "Linker", +PLUGIN_ACT-DAG: "Type": "UpdateLinkState" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Info": "Start", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Type": "UpdateChunks" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.foo1)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.foo*) #Rule 1, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "RemoveChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.foo2)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.foo*) #Rule 1, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "RemoveChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Info": "End", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Type": "UpdateChunks" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Info": "Start", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Type": "UpdateChunks" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.bar1)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "RemoveChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.bar2)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "RemoveChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.bar1)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "AddChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.bar2)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "AddChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.foo1)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "AddChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Fragment": "{{.*}}1.o(.text.foo2)", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Rule": "*(.text.bar*) #Rule 3, {{.*}}script.t", +PLUGIN_ACT-DAG: "Type": "AddChunk" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Info": "End", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Type": "UpdateChunks" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "Plugin": "OutSectIterPluginActLog", +PLUGIN_ACT-DAG: "Symbol": "baz", +PLUGIN_ACT-DAG: "Type": "RemoveSymbol" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "NewState": "CreatingSegments", +PLUGIN_ACT-DAG: "Plugin": "Linker", +PLUGIN_ACT-DAG: "Type": "UpdateLinkState" +PLUGIN_ACT: }, +PLUGIN_ACT: { +PLUGIN_ACT-DAG: "Annotation": "", +PLUGIN_ACT-DAG: "NewState": "AfterLayout", +PLUGIN_ACT-DAG: "Plugin": "Linker", +PLUGIN_ACT-DAG: "Type": "UpdateLinkState" +PLUGIN_ACT: } +PLUGIN_ACT: ] + +PLUGINS_INFO: "Plugins": [ +PLUGINS_INFO: { +PLUGINS_INFO-DAG: "Autloaded": false, +PLUGINS_INFO-DAG: "Library": "{{.*}}libPluginActivityLogPlugin{{.*}}", +PLUGINS_INFO-DAG: "Name": "OutSectIterPluginActLog", +PLUGINS_INFO-DAG: "RegisteredCommandLineOptions": [] +PLUGINS_INFO: } +PLUGINS_INFO: { +PLUGINS_INFO-DAG: "Autloaded": false, +PLUGINS_INFO-DAG: "Library": "{{.*}}libPluginActivityLogPlugin{{.*}}", +PLUGINS_INFO-DAG: "Name": "LPPluginActLog", +PLUGINS_INFO-DAG: "RegisteredCommandLineOptions": [ +PLUGINS_INFO-DAG: { +PLUGINS_INFO-DAG: "HasValue": true, +PLUGINS_INFO-DAG: "Option": "--custom-plugin-option" +PLUGINS_INFO-DAG: } +PLUGINS_INFO-DAG: ] +PLUGINS_INFO: } + +PLUGINS_INFO: ] diff --git a/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogPlugin.cpp b/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogPlugin.cpp new file mode 100644 index 000000000..303fc9866 --- /dev/null +++ b/test/Common/Plugin/PluginActivityLogFile/PluginActivityLogPlugin.cpp @@ -0,0 +1,114 @@ +// A LinkerPlugin that exercises various PluginOp operations so that +// PluginActivityLog captures them. This is a minimal smoke plugin. + +// Exercise multiple PluginOp recording paths using a LinkerPlugin. + +#include "Defines.h" +#include "LinkerPlugin.h" +#include "LinkerWrapper.h" +#include "OutputSectionIteratorPlugin.h" +#include "PluginADT.h" +#include "PluginBase.h" +#include "PluginVersion.h" +#include + +class DLL_A_EXPORT OutSectIterPluginActLog + : public eld::plugin::OutputSectionIteratorPlugin { +public: + eld::plugin::Section bazSection; + + OutSectIterPluginActLog() + : OutputSectionIteratorPlugin("OutSectIterPluginActLog") {} + + std::string GetName() override { return "OutSectIterPluginActLog"; } + + void Init(std::string) override { + if (!getLinker()->isLinkStateBeforeLayout()) + return; + auto inputFiles = getLinker()->getInputFiles(); + for (auto IF : inputFiles) { + for (auto S : IF.getSections()) { + if (S.getName().find(".baz") != std::string::npos) { + bazSection = S; + break; + } + } + if (bazSection) { + auto expSetOutSect = getLinker()->setOutputSection(bazSection, ".bar"); + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), expSetOutSect); + auto expFinishAssignOutSect = getLinker()->finishAssignOutputSections(); + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), + expFinishAssignOutSect); + } + } + } + + void processOutputSection(eld::plugin::OutputSection O) override { return; } + + // Before section merging: perform section overrides and try removing a chunk. + Status Run(bool verbose) override { + if (!getLinker()->isLinkStateCreatingSections()) + return Status::SUCCESS; + auto fooOutSect = getLinker()->getOutputSection(".foo"); + auto barOutSect = getLinker()->getOutputSection(".bar"); + auto fooRules = fooOutSect->getLinkerScriptRules(); + auto barRules = barOutSect->getLinkerScriptRules(); + auto fooChunks = fooRules.front().getChunks(); + auto barChunks = barRules.front().getChunks(); + barChunks.insert(barChunks.end(), fooChunks.begin(), fooChunks.end()); + + auto expFooRulesUpdate = getLinker()->updateChunks(fooRules.front(), {}); + ELDEXP_REPORT_AND_RETURN_ERROR_IF_ERROR(getLinker(), expFooRulesUpdate); + + auto expBarRulesUpdate = + getLinker()->updateChunks(barRules.front(), barChunks); + ELDEXP_REPORT_AND_RETURN_ERROR_IF_ERROR(getLinker(), expBarRulesUpdate); + + auto expBazSymbol = getLinker()->getSymbol("baz"); + ELDEXP_REPORT_AND_RETURN_ERROR_IF_ERROR(getLinker(), expBazSymbol); + getLinker()->removeSymbolTableEntry(expBazSymbol.value()); + getLinker()->addLinkStat("PluginName", "OutSectIterPluginActLog"); + + return Status::SUCCESS; + } + + uint32_t GetLastError() override { return Status::SUCCESS; } + + std::string GetLastErrorAsString() override { return "Success"; } + + void Destroy() override {} +}; + +class LPPluginActLog : public eld::plugin::LinkerPlugin { +public: + LPPluginActLog() : eld::plugin::LinkerPlugin("LPPluginActLog") {} + + void Init(const std::string &) override { + auto expAddOption = getLinker()->registerCommandLineOption( + "--custom-plugin-option", /*HasValue=*/true, + [](const std::string &, const std::optional &) {}); + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), expAddOption); + } +}; + +std::unordered_map plugins; + +extern "C" { +bool DLL_A_EXPORT RegisterAll() { + plugins["OutSectIterPluginActLog"] = new OutSectIterPluginActLog(); + plugins["LPPluginActLog"] = new LPPluginActLog(); + return true; +} + +eld::plugin::PluginBase DLL_A_EXPORT *getPlugin(const char *name) { + return plugins[name]; +} + +void DLL_A_EXPORT Cleanup() { + for (auto &p : plugins) { + delete p.second; + p.second = nullptr; + } + plugins.clear(); +} +}