Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions include/eld/Object/ObjectLinker.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,6 @@ class ObjectLinker {
bool mergeInputSections(ObjectBuilder &Builder,
std::vector<Section *> &Sections);

bool mayBeSortSections(std::vector<Section *> &Sections);

bool createOutputSection(ObjectBuilder &Builder, OutputSectionEntry *Output,
bool PostLayout = false);

Expand Down Expand Up @@ -320,6 +318,9 @@ class ObjectLinker {
return AllInputSections;
}

void sortAllInputSections(
std::function<bool(const Section *, const Section *)> cmp);

void addInputSection(Section *InputSection) {
AllInputSections.push_back(InputSection);
}
Expand Down
1 change: 1 addition & 0 deletions include/eld/Plugin/PluginOp.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class PluginOp {
UpdateLinkStat,
UpdateRule,
RelocationData,
SortInputSectionsForMerging
};

explicit PluginOp(plugin::LinkerWrapper *, PluginOpType T,
Expand Down
46 changes: 46 additions & 0 deletions include/eld/PluginAPI/LinkerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,52 @@ class DLL_A_EXPORT LinkerWrapper {
/// \note This function must only be used in \em BeforeLayout link state.
eld::Expected<void> finishAssignOutputSections();

/// Comparator used by \ref sortInputSectionsForSectionMerging to order the
/// input section vector that is used by the section-merging step.
using InputSectionComparator =
std::function<bool(const plugin::Section &, const plugin::Section &)>;

/// 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<std::vector<plugin::Section>>
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<void>
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.
Expand Down
11 changes: 11 additions & 0 deletions include/eld/PluginAPI/PluginADT.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();

Expand Down
26 changes: 26 additions & 0 deletions lib/LinkerWrapper/LinkerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -41,6 +42,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <memory>
#include <string>

Expand Down Expand Up @@ -211,6 +213,30 @@ eld::Expected<void> LinkerWrapper::finishAssignOutputSections() {
return {};
}

eld::Expected<std::vector<plugin::Section>>
LinkerWrapper::getInputSectionsForSectionMerging() const {
CHECK_LINK_STATE(*this, "BeforeLayout");
const auto &AllInputSections =
m_Module.getLinker()->getObjLinker()->getAllInputSections();
std::vector<plugin::Section> Sections;
Sections.reserve(AllInputSections.size());
for (eld::Section *S : AllInputSections)
Sections.emplace_back(S);
return Sections;
}

eld::Expected<void>
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<eld::Section *>(A)},
plugin::Section{const_cast<eld::Section *>(B)});
});
return {};
}

eld::Expected<void> LinkerWrapper::reassignVirtualAddresses() {
CHECK_LINK_STATE(*this, "CreatingSegments");
m_Module.getBackend().createScriptProgramHdrs();
Expand Down
20 changes: 20 additions & 0 deletions lib/LinkerWrapper/PluginADT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CommonELFSection>(m_Section))
return plugin::InputFile(commonSect->getOrigin());
return plugin::InputFile(m_Section->originalInput());
}

bool plugin::Section::isELFSection() const {
ELFSection *ELFSect = llvm::dyn_cast<ELFSection>(m_Section);
return (ELFSect != nullptr);
Expand Down Expand Up @@ -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<eld::ELFObjectFile>(m_InputFile);
if (!ObjFile)
return false;
return ObjFile->isLTOObject();
}

std::string plugin::InputFile::getMemberName() const {
if (!isArchive())
return "";
Expand Down
49 changes: 5 additions & 44 deletions lib/Object/ObjectLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,44 +756,6 @@ void ObjectLinker::markDiscardFileFormatSections() {
}
}

bool ObjectLinker::mayBeSortSections(std::vector<Section *> &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<ELFSection>(ASection);
ELFSection *B = llvm::dyn_cast<ELFSection>(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<Section *> &Sections) {
bool IsPartialLink = ThisConfig.isLinkPartial();
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -4144,3 +4100,8 @@ bool ObjectLinker::initializeTarget(InputFile *I) {
return false;
return true;
}

void ObjectLinker::sortAllInputSections(
std::function<bool(const Section *, const Section *)> cmp) {
std::stable_sort(AllInputSections.begin(), AllInputSections.end(), cmp);
}
1 change: 1 addition & 0 deletions lib/Plugin/PluginOp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ ResetOffsetPluginOp::ResetOffsetPluginOp(plugin::LinkerWrapper *W,
const std::string &Annotation)
: PluginOp(W, PluginOp::ResetOffset, Annotation), O(O),
OldOffset(OldOffset) {}

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__attribute__((section(".ctors"))) int bar() { return 6; }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
int baz() { return 5; }
Original file line number Diff line number Diff line change
@@ -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")
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SECTIONS {
.text : { *(.text*) }
}
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions test/Common/Plugin/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 21 additions & 0 deletions test/Common/Plugin/GetRuleMatchingInput/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
18 changes: 18 additions & 0 deletions test/Common/Plugin/GetRuleMatchingInput/GetRuleMatchingInput.test
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading