diff --git a/include/eld/Object/ObjectLinker.h b/include/eld/Object/ObjectLinker.h index d3411ad72..a9e64ae39 100644 --- a/include/eld/Object/ObjectLinker.h +++ b/include/eld/Object/ObjectLinker.h @@ -258,8 +258,6 @@ class ObjectLinker { bool mergeInputSections(ObjectBuilder &Builder, std::vector
&Sections); - bool mayBeSortSections(std::vector
&Sections); - bool createOutputSection(ObjectBuilder &Builder, OutputSectionEntry *Output, bool PostLayout = false); @@ -320,6 +318,9 @@ class ObjectLinker { return AllInputSections; } + void sortAllInputSections( + std::function cmp); + void addInputSection(Section *InputSection) { AllInputSections.push_back(InputSection); } diff --git a/include/eld/Plugin/PluginOp.h b/include/eld/Plugin/PluginOp.h index 384497083..ea3f56c13 100644 --- a/include/eld/Plugin/PluginOp.h +++ b/include/eld/Plugin/PluginOp.h @@ -34,6 +34,7 @@ class PluginOp { UpdateLinkStat, UpdateRule, RelocationData, + SortInputSectionsForMerging }; explicit PluginOp(plugin::LinkerWrapper *, PluginOpType T, diff --git a/include/eld/PluginAPI/LinkerWrapper.h b/include/eld/PluginAPI/LinkerWrapper.h index 2a2e24c07..d67e2d24f 100644 --- a/include/eld/PluginAPI/LinkerWrapper.h +++ b/include/eld/PluginAPI/LinkerWrapper.h @@ -248,6 +248,52 @@ class DLL_A_EXPORT LinkerWrapper { /// \note This function must only be used in \em BeforeLayout link state. eld::Expected finishAssignOutputSections(); + /// Comparator used by \ref sortInputSectionsForSectionMerging to order the + /// input section vector that is used by the section-merging step. + using InputSectionComparator = + std::function; + + /// Returns the input sections vector that the linker will consume for + /// the section-merging step. + /// + /// For each linker script rule, the section-merging step + /// merges the matched input sections and place them into the + /// rule. The order of the input sections in the rule, and consequently, + /// the output image, depends upon this input sections vector. + /// + /// By default, the order of input sections in this vector is + /// the input order, that is, + /// [Input[0].sections..., Input[1].sections..., Input[2].sections..., ...]. + /// + /// \note This function must only be used in the + /// \em ActBeforeSectionMerging link state. + eld::Expected> + getInputSectionsForSectionMerging() const; + + /// Stable sort the input sections vector that will be used for the + /// section-merging step. The order of equivalent elements as per the + /// comparator is guaranteed to be preserved. + /// + /// By default, the order of input sections in this vector is + /// the input order, that is, + /// [Input[0].sections..., Input[1].sections..., Input[2].sections..., ...]. + /// + /// The sort is performed with \c std::stable_sort, so input sections that + /// compare equivalent under \p Cmp retain their relative order from the + /// pre-sort list. \p Cmp must define a strict weak ordering. + /// + /// \param cmp A comparator returning \c true when the first argument is + /// less than (is ordered before) the second argument. + /// + /// \param annotation Optional human-readable note recorded in the plugin + /// activity log alongside this call. + /// + /// \note This function must only be used in the + /// \em ActBeforeSectionMerging link state. + eld::Expected + sortInputSectionsForSectionMerging(InputSectionComparator cmp, + std::string_view annotation = ""); + /// This function may be called to reassign section addresses to reflect /// newly added output sections that have not yet been assigned an address. /// \note This function may only be used in \em CreatingSegments state. diff --git a/include/eld/PluginAPI/PluginADT.h b/include/eld/PluginAPI/PluginADT.h index 59192e947..3c5e2ade0 100644 --- a/include/eld/PluginAPI/PluginADT.h +++ b/include/eld/PluginAPI/PluginADT.h @@ -747,6 +747,13 @@ struct DLL_A_EXPORT Section { /// returns false otherwise. bool hasOldInputFile() const; + /// Returns the input file used for rule matching for this section. + /// If a rule-matching input was explicitly set via + /// LinkerWrapper::setRuleMatchingInput, that input is returned; otherwise + /// the section's current input file is returned. Returns a null InputFile + /// if the object is an empty handler. + plugin::InputFile getRuleMatchingInput() const; + /// Returns the hash of the input section. uint64_t getSectionHash() const; @@ -1236,6 +1243,10 @@ struct DLL_A_EXPORT InputFile { /// Returns true if the input is LLVM bitcode; Otherwise returns false. bool isBitcode() const; + /// Returns true if the input file is an ELF object file generated by LTO; + /// Otherwise returns false. + bool isLTOGeneratedObject() const; + /// Returns true if the inputFile is an objectFile; Otherwise retruns false. bool isObjectFile(); diff --git a/lib/LinkerWrapper/LinkerWrapper.cpp b/lib/LinkerWrapper/LinkerWrapper.cpp index 4bfeb69c3..540d00a56 100644 --- a/lib/LinkerWrapper/LinkerWrapper.cpp +++ b/lib/LinkerWrapper/LinkerWrapper.cpp @@ -17,6 +17,7 @@ #include "eld/Object/OutputSectionEntry.h" #include "eld/Object/SectionMap.h" #include "eld/Plugin/PluginManager.h" +#include "eld/Plugin/PluginOp.h" #include "eld/PluginAPI/DWARF.h" #include "eld/PluginAPI/DiagnosticEntry.h" #include "eld/PluginAPI/Diagnostics.h" @@ -41,6 +42,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Process.h" #include "llvm/Support/raw_ostream.h" +#include #include #include @@ -211,6 +213,30 @@ eld::Expected LinkerWrapper::finishAssignOutputSections() { return {}; } +eld::Expected> +LinkerWrapper::getInputSectionsForSectionMerging() const { + CHECK_LINK_STATE(*this, "BeforeLayout"); + const auto &AllInputSections = + m_Module.getLinker()->getObjLinker()->getAllInputSections(); + std::vector Sections; + Sections.reserve(AllInputSections.size()); + for (eld::Section *S : AllInputSections) + Sections.emplace_back(S); + return Sections; +} + +eld::Expected +LinkerWrapper::sortInputSectionsForSectionMerging(InputSectionComparator cmp, + std::string_view annotation) { + CHECK_LINK_STATE(*this, "BeforeLayout"); + m_Module.getLinker()->getObjLinker()->sortAllInputSections( + [&cmp](const eld::Section *A, const eld::Section *B) { + return cmp(plugin::Section{const_cast(A)}, + plugin::Section{const_cast(B)}); + }); + return {}; +} + eld::Expected LinkerWrapper::reassignVirtualAddresses() { CHECK_LINK_STATE(*this, "CreatingSegments"); m_Module.getBackend().createScriptProgramHdrs(); diff --git a/lib/LinkerWrapper/PluginADT.cpp b/lib/LinkerWrapper/PluginADT.cpp index bcd9635c7..f28f658fa 100644 --- a/lib/LinkerWrapper/PluginADT.cpp +++ b/lib/LinkerWrapper/PluginADT.cpp @@ -710,6 +710,17 @@ bool plugin::Section::hasOldInputFile() const { return (ELFSect->hasOldInputFile()); } +plugin::InputFile plugin::Section::getRuleMatchingInput() const { + if (!m_Section) + return plugin::InputFile(nullptr); + if (m_Section->hasOldInputFile()) + return plugin::InputFile(m_Section->originalInput()); + if (CommonELFSection *commonSect = + llvm::dyn_cast(m_Section)) + return plugin::InputFile(commonSect->getOrigin()); + return plugin::InputFile(m_Section->originalInput()); +} + bool plugin::Section::isELFSection() const { ELFSection *ELFSect = llvm::dyn_cast(m_Section); return (ELFSect != nullptr); @@ -1376,6 +1387,15 @@ bool plugin::InputFile::isBitcode() const { return m_InputFile->isBitcode(); } +bool plugin::InputFile::isLTOGeneratedObject() const { + if (!m_InputFile) + return false; + eld::ELFObjectFile *ObjFile = llvm::dyn_cast(m_InputFile); + if (!ObjFile) + return false; + return ObjFile->isLTOObject(); +} + std::string plugin::InputFile::getMemberName() const { if (!isArchive()) return ""; diff --git a/lib/Object/ObjectLinker.cpp b/lib/Object/ObjectLinker.cpp index 58b8c819a..01cf964db 100644 --- a/lib/Object/ObjectLinker.cpp +++ b/lib/Object/ObjectLinker.cpp @@ -756,44 +756,6 @@ void ObjectLinker::markDiscardFileFormatSections() { } } -bool ObjectLinker::mayBeSortSections(std::vector
&Sections) { - // If no linker scripts, we dont store the original input. Lets not sort. - if (!ThisModule->getScript().linkerScriptHasSectionsCommand()) - return true; - if (ThisConfig.options().disableLTOLinkOrder()) - return true; - // If we are doing partial link, lets not sort it. - bool IsPartialLink = (LinkerConfig::Object == ThisConfig.codeGenType()); - if (IsPartialLink || LtoObjects.empty()) - return true; - std::stable_sort(Sections.begin(), Sections.end(), - [](Section *ASection, Section *BSection) { - ELFSection *A = llvm::dyn_cast(ASection); - ELFSection *B = llvm::dyn_cast(BSection); - if (A == nullptr or B == nullptr) - return false; - // FIXME: Redundant checks. All files have original input. - if (!A->originalInput()) - return false; - if (!B->originalInput()) - return false; - if ((A->name().starts_with(".ctors")) || - (B->name().starts_with(".ctors"))) - return false; - if ((A->name().starts_with(".dtors")) || - (B->name().starts_with(".dtors"))) - return false; - int64_t AOrdinal = - A->originalInput()->getInput()->getInputOrdinal(); - int64_t BOrdinal = - B->originalInput()->getInput()->getInputOrdinal(); - if (AOrdinal == BOrdinal) - return false; - return (AOrdinal < BOrdinal); - }); - return true; -} - bool ObjectLinker::mergeInputSections(ObjectBuilder &Builder, std::vector
&Sections) { bool IsPartialLink = ThisConfig.isLinkPartial(); @@ -1086,12 +1048,6 @@ bool ObjectLinker::initializeMerge() { } } } - { - eld::RegisterTimer T("Sort sections if LTO enabled", "Merge Sections", - ThisConfig.options().printTimingStats()); - // Sort sections if we have LTO enabled. - mayBeSortSections(AllInputSections); - } return true; } @@ -4144,3 +4100,8 @@ bool ObjectLinker::initializeTarget(InputFile *I) { return false; return true; } + +void ObjectLinker::sortAllInputSections( + std::function cmp) { + std::stable_sort(AllInputSections.begin(), AllInputSections.end(), cmp); +} \ No newline at end of file diff --git a/lib/Plugin/PluginOp.cpp b/lib/Plugin/PluginOp.cpp index e76fa797d..1f1bb57e5 100644 --- a/lib/Plugin/PluginOp.cpp +++ b/lib/Plugin/PluginOp.cpp @@ -72,3 +72,4 @@ ResetOffsetPluginOp::ResetOffsetPluginOp(plugin::LinkerWrapper *W, const std::string &Annotation) : PluginOp(W, PluginOp::ResetOffset, Annotation), O(O), OldOffset(OldOffset) {} + diff --git a/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/2.c b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/2.c new file mode 100644 index 000000000..88cc62fb2 --- /dev/null +++ b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/2.c @@ -0,0 +1 @@ +__attribute__((section(".ctors"))) int bar() { return 6; } diff --git a/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/3.c b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/3.c new file mode 100644 index 000000000..db10c8ee3 --- /dev/null +++ b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/3.c @@ -0,0 +1 @@ +int baz() { return 5; } diff --git a/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/gen.py b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/gen.py new file mode 100644 index 000000000..cfc29b449 --- /dev/null +++ b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/gen.py @@ -0,0 +1,16 @@ +import sys + +N = 300 +output = sys.argv[1] + +with open(output, 'w') as f: + f.write("int bar();\n") + f.write("int baz();\n") + for i in range(N): + f.write(f"int foo_{i}() {{ return {i}; }}\n") + f.write("int main() {\n return ") + for i in range(N): + f.write(f"foo_{i}()") + if i != N - 1: + f.write(" + ") + f.write(" + baz() + bar();\n}\n") diff --git a/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/script.t b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/script.t new file mode 100644 index 000000000..5eb558f70 --- /dev/null +++ b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/Inputs/script.t @@ -0,0 +1,3 @@ +SECTIONS { + .text : { *(.text*) } +} diff --git a/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/NonLTOSectionsOrderWithLTO.test b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/NonLTOSectionsOrderWithLTO.test new file mode 100644 index 000000000..e208c73ab --- /dev/null +++ b/test/Common/LTO/LinkerScript/NonLTOSectionsOrderWithLTO/NonLTOSectionsOrderWithLTO.test @@ -0,0 +1,42 @@ +UNSUPPORTED: x86 +#---NonLTOSectionsOrderWithLTO.test------------- Executable,LTO,LS ----------------# +#BEGIN_COMMENT +# When a linker script rule matches input sections without explicit ordering, +# the output should contain matched input sections in input order. With LTO, +# ordering is relaxed for LTO-generated sections, but sections from non-LTO +# object files must still maintain their original order. This test verifies +# that non-LTO input section ordering is preserved when mixed with LTO objects. +#END_COMMENT +#START_TEST +RUN: %python %p/Inputs/gen.py %t1.1.c +RUN: %clang %clangopts -o %t1.1.o -c %t1.1.c -ffunction-sections +RUN: %clang %clangopts -o %t1.2.o -c %p/Inputs/2.c -flto -ffunction-sections +RUN: %clang %clangopts -o %t1.3.o -c %p/Inputs/3.c -flto -ffunction-sections +RUN: %link -MapStyle txt %linkopts -o %t2.out %t1.2.o %t1.1.o %t1.3.o -T %p/Inputs/script.t -e main -Map %t2.map +RUN: %filecheck %s < %t2.map +#END_TEST + +CHECK: .text.foo_0 +CHECK: .text.foo_1 +CHECK: .text.foo_2 +CHECK: .text.foo_3 +CHECK: .text.foo_4 +CHECK: .text.foo_5 +CHECK: .text.foo_6 +CHECK: .text.foo_7 +CHECK: .text.foo_8 +CHECK: .text.foo_9 +CHECK: .text.foo_10 +CHECK: .text.foo_11 +CHECK: .text.foo_12 +CHECK: .text.foo_13 +CHECK: .text.foo_14 +CHECK: .text.foo_15 +CHECK: .text.foo_16 +CHECK: .text.foo_17 +CHECK: .text.foo_18 +CHECK: .text.foo_19 +CHECK: .text.foo_20 +CHECK: .text.foo_298 +CHECK: .text.foo_299 +CHECK: .text.main diff --git a/test/Common/Plugin/CMakeLists.txt b/test/Common/Plugin/CMakeLists.txt index d04ac4b15..efc615ab3 100644 --- a/test/Common/Plugin/CMakeLists.txt +++ b/test/Common/Plugin/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(GetLinkerVersion) add_subdirectory(GetEnv) add_subdirectory(GetOutputSection) add_subdirectory(GetInputSectionDescription) +add_subdirectory(GetRuleMatchingInput) add_subdirectory(GetUses) add_subdirectory(InputSectionAPIs) add_subdirectory(GetInputSectionHash) @@ -28,6 +29,7 @@ add_subdirectory(INIFile) add_subdirectory(InputFiles) add_subdirectory(InputSpecAPITests) add_subdirectory(InputFilePluginAPIs) +add_subdirectory(IsLTOGeneratedObject) add_subdirectory(InvalidDiagnostics) add_subdirectory(InvalidOutputSectionOverride) add_subdirectory(InvalidStateOverrideLSRule) @@ -74,6 +76,7 @@ add_subdirectory(RuleMatchingSectNameMapErrors) add_subdirectory(SearchDiagnosticsPlugin) add_subdirectory(SectionTypes) add_subdirectory(SignedDiagnostics) +add_subdirectory(SortInputSectionsForMerging) add_subdirectory(TarWriterTests) add_subdirectory(TimingReport) add_subdirectory(UnbalancedChunkMoves) diff --git a/test/Common/Plugin/GetRuleMatchingInput/CMakeLists.txt b/test/Common/Plugin/GetRuleMatchingInput/CMakeLists.txt new file mode 100644 index 000000000..4b3b77b6a --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCES GetRuleMatchingInputPlugin.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(GetRuleMatchingInputPlugin ${SHARED_LIB_SOURCES} + LINK_LIBS LW) + + set_target_properties( + GetRuleMatchingInputPlugin + PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/test") + + set(BUILD_SHARED_LIBS ${bsl}) +endif() + +add_plugin(GetRuleMatchingInputPlugin) diff --git a/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInput.test b/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInput.test new file mode 100644 index 000000000..42a2a7017 --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInput.test @@ -0,0 +1,18 @@ +#---GetRuleMatchingInput.test----------------------- Executable --------------------# +#BEGIN_COMMENT +# This test verifies the behavior of the LinkerWrapper::getRuleMatchingInput API. +# A section whose rule-matching input was overridden via setRuleMatchingInput +# returns the overridden input; a section without an override falls back to its +# current input file; and an empty section returns a null input file. +#END_COMMENT +#START_TEST +RUN: %clang %clangopts -o %t1.1.o %p/Inputs/1.c -c -ffunction-sections -fcommon +RUN: %clang %clangopts -o %t1.2.o %p/Inputs/2.c -c -ffunction-sections -fcommon +RUN: %link %linkopts -o %t1.1.out %t1.1.o %t1.2.o \ +RUN: -L%libsdir/test --plugin-config %p/Inputs/PluginConfig.yaml | %filecheck %s +#END_TEST + +CHECK: BarSect rule-matching input: {{.*}}1.2.o +CHECK: FooSect rule-matching input: {{.*}}1.1.o +CHECK: Empty section rule-matching input: '' +CHECK: Empty section rule-matching input: {{.*}}1.o diff --git a/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInputPlugin.cpp b/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInputPlugin.cpp new file mode 100644 index 000000000..8908cdc41 --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInputPlugin.cpp @@ -0,0 +1,68 @@ +#include "LinkerPlugin.h" +#include "LinkerWrapper.h" +#include "PluginADT.h" +#include "PluginVersion.h" +#include + +class GetRuleMatchingInputPlugin : public eld::plugin::LinkerPlugin { +public: + GetRuleMatchingInputPlugin() + : eld::plugin::LinkerPlugin("GetRuleMatchingInputPlugin") {} + + void VisitSections(eld::plugin::InputFile IF) override { + if (endswith(IF.getFileName(), "2.o")) { + // Remember 2.o; it will be used as the rule-matching input for a + // section that actually belongs to 1.o. + OtherInput = IF; + return; + } + if (endswith(IF.getFileName(), "1.o")) { + for (auto S : IF.getSections()) { + if (S.getName() == ".text.foo") + FooSect = S; + else if (S.getName() == ".text.bar") + BarSect = S; + } + } + } + + void ActBeforeRuleMatching() override { + auto expComSym = getLinker()->getSymbol("C"); + ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), expComSym); + + eld::plugin::Symbol comSym = expComSym.value(); + + // Override the rule-matching input of .text.bar (from 1.o) with 2.o. + getLinker()->setRuleMatchingInput(BarSect, OtherInput); + + // .text.bar has an explicit rule-matching input, so we expect 2.o. + std::cout << "BarSect rule-matching input: " + << BarSect.getRuleMatchingInput().getFileName() << "\n"; + + // .text.foo has no override, so it falls back to its current input (1.o). + std::cout << "FooSect rule-matching input: " + << FooSect.getRuleMatchingInput().getFileName() << "\n"; + + // An empty section yields a null input file (empty file name). + eld::plugin::Section EmptySect; + std::cout << "Empty section rule-matching input: '" + << EmptySect.getRuleMatchingInput().getFileName() << "'\n"; + + // Common symbol rule-mathcing input + eld::plugin::Section comSymSect = comSym.getChunk().getSection(); + std::cout << "Empty section rule-matching input: " + << comSymSect.getRuleMatchingInput().getFileName() << "\n"; + } + +private: + bool endswith(const std::string &s, const std::string &suffix) { + if (s.size() < suffix.size()) + return false; + return s.substr(s.size() - suffix.size()) == suffix; + } + eld::plugin::Section FooSect; + eld::plugin::Section BarSect; + eld::plugin::InputFile OtherInput{nullptr}; +}; + +ELD_REGISTER_PLUGIN(GetRuleMatchingInputPlugin) diff --git a/test/Common/Plugin/GetRuleMatchingInput/Inputs/1.c b/test/Common/Plugin/GetRuleMatchingInput/Inputs/1.c new file mode 100644 index 000000000..529682640 --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/Inputs/1.c @@ -0,0 +1,3 @@ +int foo() { return 1; } +int bar() { return 3; } +int C; \ No newline at end of file diff --git a/test/Common/Plugin/GetRuleMatchingInput/Inputs/2.c b/test/Common/Plugin/GetRuleMatchingInput/Inputs/2.c new file mode 100644 index 000000000..db10c8ee3 --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/Inputs/2.c @@ -0,0 +1 @@ +int baz() { return 5; } diff --git a/test/Common/Plugin/GetRuleMatchingInput/Inputs/PluginConfig.yaml b/test/Common/Plugin/GetRuleMatchingInput/Inputs/PluginConfig.yaml new file mode 100644 index 000000000..04af7dc7f --- /dev/null +++ b/test/Common/Plugin/GetRuleMatchingInput/Inputs/PluginConfig.yaml @@ -0,0 +1,4 @@ +GlobalPlugins: + - Type: LinkerPlugin + Name: GetRuleMatchingInputPlugin + Library: GetRuleMatchingInputPlugin diff --git a/test/Common/Plugin/IsLTOGeneratedObject/CMakeLists.txt b/test/Common/Plugin/IsLTOGeneratedObject/CMakeLists.txt new file mode 100644 index 000000000..ce15ea897 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/CMakeLists.txt @@ -0,0 +1,16 @@ +set(SOURCES IsLTOGeneratedObject.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(IsLTOGeneratedObject ${SHARED_LIB_SOURCES} LINK_LIBS LW) + + set(BUILD_SHARED_LIBS ${bsl}) + +endif() + +add_plugin(IsLTOGeneratedObject) diff --git a/test/Common/Plugin/IsLTOGeneratedObject/Inputs/bar.c b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/bar.c new file mode 100644 index 000000000..75dae47b6 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/bar.c @@ -0,0 +1 @@ +int bar() { return 2; } diff --git a/test/Common/Plugin/IsLTOGeneratedObject/Inputs/foo.c b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/foo.c new file mode 100644 index 000000000..b8c770679 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/foo.c @@ -0,0 +1,2 @@ +int bar(); +int foo() { return bar(); } diff --git a/test/Common/Plugin/IsLTOGeneratedObject/Inputs/plugin.config b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/plugin.config new file mode 100644 index 000000000..91e704c66 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/Inputs/plugin.config @@ -0,0 +1,4 @@ +GlobalPlugins: + - Type : LinkerPlugin + Name : IsLTOGeneratedObjectPlugin + Library: IsLTOGeneratedObject diff --git a/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.cpp b/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.cpp new file mode 100644 index 000000000..731a232c3 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.cpp @@ -0,0 +1,25 @@ +#include "Defines.h" +#include "LinkerPlugin.h" +#include "LinkerWrapper.h" +#include "PluginADT.h" +#include "PluginVersion.h" +#include + +using namespace eld::plugin; + +class DLL_A_EXPORT IsLTOGeneratedObjectPlugin : public LinkerPlugin { +public: + IsLTOGeneratedObjectPlugin() : LinkerPlugin("IsLTOGeneratedObjectPlugin") {} + + void ActBeforeSectionMerging() override { + for (auto &I : getLinker()->getInputFiles()) { + if (!I.isObjectFile()) + continue; + std::cout << I.getFileName() + << " isLTOGeneratedObject: " << I.isLTOGeneratedObject() + << "\n"; + } + } +}; + +ELD_REGISTER_PLUGIN(IsLTOGeneratedObjectPlugin) diff --git a/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.test b/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.test new file mode 100644 index 000000000..3ccaf5333 --- /dev/null +++ b/test/Common/Plugin/IsLTOGeneratedObject/IsLTOGeneratedObject.test @@ -0,0 +1,23 @@ +#---IsLTOGeneratedObject.test----------------------- Executable,LS ----------------# +#BEGIN_COMMENT +# Tests that isLTOGeneratedObject() returns true for ELF objects generated +# by LTO (full LTO, ThinLTO, and fat LTO) and false for regular ELF object files. +#END_COMMENT +#START_TEST + +## Full LTO: foo.c is a regular ELF, bar.c is bitcode (-flto). +RUN: %clang %clangopts -c %p/Inputs/foo.c -o %t1.foo.o +RUN: %clang %clangopts -c -flto %p/Inputs/bar.c -o %t1.bar.o +RUN: %link %linkopts %t1.foo.o %t1.bar.o -o %t2.out \ +RUN: --plugin-config %p/Inputs/plugin.config 2>&1 | %filecheck %s + +## ThinLTO: foo.c is a regular ELF, bar.c is thin bitcode (-flto=thin). +RUN: %clang %clangopts -c -flto=thin %p/Inputs/bar.c -o %t1.thin.bar.o +RUN: %link %linkopts %t1.foo.o %t1.thin.bar.o -o %t3.out \ +RUN: --plugin-config %p/Inputs/plugin.config 2>&1 | %filecheck %s + +# Regular ELF object: isLTOGeneratedObject should be false (0). +CHECK: {{.*}}foo.o isLTOGeneratedObject: 0 +# LTO-generated ELF object produced internally during link: should be true (1). +CHECK: {{.*}} isLTOGeneratedObject: 1 +CHECK: {{.*}}bar.o isLTOGeneratedObject: 0 diff --git a/test/Common/Plugin/SortInputSectionsForMerging/CMakeLists.txt b/test/Common/Plugin/SortInputSectionsForMerging/CMakeLists.txt new file mode 100644 index 000000000..552cbcaf6 --- /dev/null +++ b/test/Common/Plugin/SortInputSectionsForMerging/CMakeLists.txt @@ -0,0 +1,21 @@ +set(SOURCES SortInputSectionsForMergingPlugin.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(SortInputSectionsForMergingPlugin ${SHARED_LIB_SOURCES} + LINK_LIBS LW) + + set_target_properties( + SortInputSectionsForMergingPlugin + PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/test") + + set(BUILD_SHARED_LIBS ${bsl}) +endif() + +add_plugin(SortInputSectionsForMergingPlugin) diff --git a/test/Common/Plugin/SortInputSectionsForMerging/Inputs/1.c b/test/Common/Plugin/SortInputSectionsForMerging/Inputs/1.c new file mode 100644 index 000000000..df771dbeb --- /dev/null +++ b/test/Common/Plugin/SortInputSectionsForMerging/Inputs/1.c @@ -0,0 +1,3 @@ +int aaa() { return 1; } +int bbb() { return 2; } +int ccc() { return 3; } diff --git a/test/Common/Plugin/SortInputSectionsForMerging/Inputs/PluginConfig.yaml b/test/Common/Plugin/SortInputSectionsForMerging/Inputs/PluginConfig.yaml new file mode 100644 index 000000000..247b6e7a0 --- /dev/null +++ b/test/Common/Plugin/SortInputSectionsForMerging/Inputs/PluginConfig.yaml @@ -0,0 +1,4 @@ +GlobalPlugins: + - Type: LinkerPlugin + Name: SortInputSectionsForMergingPlugin + Library: SortInputSectionsForMergingPlugin diff --git a/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMerging.test b/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMerging.test new file mode 100644 index 000000000..dd7b57b9b --- /dev/null +++ b/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMerging.test @@ -0,0 +1,31 @@ +#---SortInputSectionsForMerging.test---------------------- Executable,LS --------------------# +#BEGIN_COMMENT +# Tests that LinkerWrapper::getInputSectionsForSectionMerging exposes the +# pre-merge input section pool, and that +# LinkerWrapper::sortInputSectionsForSectionMerging reorders that pool using a +# user-supplied comparator. The plugin sorts by descending section name, +# so .text.ccc should appear before .text.bbb, which should appear before +# .text.aaa after the sort. +#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 --plugin-config \ +RUN: %p/Inputs/PluginConfig.yaml -Map %t1.1.map.txt \ +RUN: 2>&1 | %filecheck %s +RUN: %filecheck %s --check-prefix=MAP < %t1.1.map.txt +#END_TEST + +CHECK: Before sort: +CHECK-DAG: .text.aaa +CHECK-DAG: .text.bbb +CHECK-DAG: .text.ccc +CHECK: After sort: +CHECK: .text.ccc +CHECK: .text.bbb +CHECK: .text.aaa + +MAP: Output Section and Layout +MAP: .text +MAP: .text.ccc +MAP: .text.bbb +MAP: .text.aaa diff --git a/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMergingPlugin.cpp b/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMergingPlugin.cpp new file mode 100644 index 000000000..7c862da7c --- /dev/null +++ b/test/Common/Plugin/SortInputSectionsForMerging/SortInputSectionsForMergingPlugin.cpp @@ -0,0 +1,50 @@ +#include "Defines.h" +#include "LinkerPlugin.h" +#include "LinkerWrapper.h" +#include "PluginADT.h" +#include "PluginVersion.h" +#include +#include +#include + +using namespace eld::plugin; + +class DLL_A_EXPORT SortInputSectionsForMergingPlugin : public LinkerPlugin { +public: + SortInputSectionsForMergingPlugin() + : LinkerPlugin("SortInputSectionsForMergingPlugin") {} + + void ActBeforeSectionMerging() override { + auto Before = getLinker()->getInputSectionsForSectionMerging(); + if (!Before) { + getLinker()->reportDiagEntry(std::move(Before.error())); + return; + } + std::cout << "Before sort:\n"; + for (const auto &S : *Before) + if (!S.getName().empty()) + std::cout << " " << S.getName() << "\n"; + + auto SortRes = getLinker()->sortInputSectionsForSectionMerging( + [](const Section &A, const Section &B) { + return A.getName() > B.getName(); + }, + "DescendingByName"); + if (!SortRes) { + getLinker()->reportDiagEntry(std::move(SortRes.error())); + return; + } + + auto After = getLinker()->getInputSectionsForSectionMerging(); + if (!After) { + getLinker()->reportDiagEntry(std::move(After.error())); + return; + } + std::cout << "After sort:\n"; + for (const auto &S : *After) + if (!S.getName().empty()) + std::cout << " " << S.getName() << "\n"; + } +}; + +ELD_REGISTER_PLUGIN(SortInputSectionsForMergingPlugin)