Skip to content

Commit da7697a

Browse files
svs-quicquic-seaswara
authored andcommitted
ELD support for LTO w/LinkerScripts
Signed-off-by: Sudharsan Veeravalli <svs@qti.qualcomm.com>
1 parent 54d6cdf commit da7697a

37 files changed

Lines changed: 1565 additions & 18 deletions
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
//===- AdvancedLTO.cpp----------------------------------------------------===//
2+
// Part of the eld Project, under the BSD License
3+
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
4+
// SPDX-License-Identifier: BSD-3-Clause
5+
//===----------------------------------------------------------------------===//
6+
7+
#include "Defines.h"
8+
#include "LinkerPlugin.h"
9+
#include "LinkerWrapper.h"
10+
#include "PluginADT.h"
11+
#include "PluginVersion.h"
12+
#include "llvm/ADT/ArrayRef.h"
13+
#include "llvm/BinaryFormat/ELF.h"
14+
#include "llvm/IR/GlobalValue.h"
15+
#include "llvm/LTO/LTO.h"
16+
#include <cassert>
17+
#include <cerrno>
18+
#include <cstdint>
19+
#include <cstdlib>
20+
#include <memory>
21+
#include <optional>
22+
#include <string>
23+
#include <unordered_map>
24+
25+
namespace {
26+
27+
using namespace eld::plugin;
28+
29+
struct ModuleData {
30+
explicit ModuleData(BitcodeFile BCF) : BCFile(BCF) {}
31+
32+
BitcodeFile BCFile;
33+
std::unordered_map<std::string, Section> SectionsByName;
34+
};
35+
36+
class DLL_A_EXPORT AdvancedLTO : public LinkerPlugin {
37+
public:
38+
AdvancedLTO() : LinkerPlugin("AdvancedLTO") {}
39+
40+
void Init(const std::string &Options) override {
41+
(void)Options;
42+
EnableLTOLinkerScripts =
43+
getLinker()->getLinkerConfig().hasLTOLinkerScripts();
44+
TraceLTO = getLinker()->getLinkerConfig().shouldTraceLTO();
45+
}
46+
47+
LTOModule *CreateLTOModule(BitcodeFile BCF, LTOModuleHash Hash) override {
48+
LastBitcodeFile = BCF;
49+
auto Data = std::make_unique<ModuleData>(BCF);
50+
LTOModule *M = reinterpret_cast<LTOModule *>(Data.get());
51+
Modules[M] = std::move(Data);
52+
FilesByHash.insert_or_assign(Hash, &BCF.getBitcodeFile());
53+
return M;
54+
}
55+
56+
void ReadSymbols(LTOModule &M) override {
57+
ModuleData *Data = importModule(M);
58+
if (!Data)
59+
return;
60+
61+
auto getKind = [](const llvm::lto::InputFile::Symbol &Sym) {
62+
if (Sym.isUndefined())
63+
return Symbol::Undefined;
64+
if (Sym.isCommon())
65+
return Symbol::Common;
66+
return Symbol::Define;
67+
};
68+
69+
auto getBinding = [](const llvm::lto::InputFile::Symbol &Sym) {
70+
if (!Sym.isGlobal())
71+
return Symbol::Local;
72+
if (Sym.isWeak())
73+
return Symbol::Weak;
74+
return Symbol::Global;
75+
};
76+
77+
auto getVisibility = [](const llvm::lto::InputFile::Symbol &Sym) {
78+
switch (Sym.getVisibility()) {
79+
case llvm::GlobalValue::DefaultVisibility:
80+
return Symbol::Default;
81+
case llvm::GlobalValue::HiddenVisibility:
82+
return Symbol::Hidden;
83+
case llvm::GlobalValue::ProtectedVisibility:
84+
return Symbol::Protected;
85+
}
86+
return Symbol::Default;
87+
};
88+
89+
auto getType = [](const llvm::lto::InputFile::Symbol &Sym) {
90+
if (Sym.isExecutable())
91+
return static_cast<unsigned>(llvm::ELF::STT_FUNC);
92+
if (Sym.isTLS())
93+
return static_cast<unsigned>(llvm::ELF::STT_TLS);
94+
return static_cast<unsigned>(llvm::ELF::STT_OBJECT);
95+
};
96+
97+
llvm::ArrayRef<llvm::lto::InputFile::Symbol> Symbols =
98+
Data->BCFile.getInputFile().symbols();
99+
100+
for (const auto &Sym : llvm::enumerate(Symbols)) {
101+
unsigned SymbolIndex = Sym.index();
102+
const llvm::lto::InputFile::Symbol &LtoSym = Sym.value();
103+
bool KeepComdat = Data->BCFile.findIfKeptComdat(LtoSym.getComdatIndex());
104+
Symbol::Kind Kind = KeepComdat ? getKind(LtoSym) : Symbol::Undefined;
105+
106+
Section InputSection;
107+
llvm::StringRef SectionName = LtoSym.getSectionName();
108+
if (SectionName.empty() && LtoSym.isCommon())
109+
SectionName = "COMMON";
110+
111+
if (!SectionName.empty()) {
112+
auto ExpSection = getOrCreateSection(*Data, SectionName);
113+
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSection);
114+
InputSection = *ExpSection;
115+
}
116+
117+
auto ExpSymbol = getLinker()->addSymbol(
118+
Data->BCFile, LtoSym.getName().str(), getBinding(LtoSym),
119+
InputSection, Kind, getVisibility(LtoSym), getType(LtoSym),
120+
LtoSym.isCommon() ? LtoSym.getCommonSize() : 0, SymbolIndex);
121+
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSymbol);
122+
123+
if (!EnableLTOLinkerScripts || !InputSection || Kind == Symbol::Undefined)
124+
continue;
125+
126+
auto Rule = InputSection.getLinkerScriptRule();
127+
if (!Rule || !Rule.isKeep())
128+
continue;
129+
130+
auto ExpSetPreserve = getLinker()->setPreserveSymbol(*ExpSymbol);
131+
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSetPreserve);
132+
}
133+
}
134+
135+
void VisitSections(InputFile IF) override {
136+
if (!EnableLTOLinkerScripts)
137+
return;
138+
139+
for (Section S : IF.getSections()) {
140+
std::string Name = S.getName();
141+
size_t FirstPos = Name.find("^^");
142+
if (FirstPos == std::string::npos)
143+
continue;
144+
std::string BaseName = Name.substr(0, FirstPos);
145+
std::string HashStr = Name.substr(FirstPos + 2);
146+
size_t SecondPos = HashStr.find("^^");
147+
if (SecondPos != std::string::npos)
148+
HashStr = HashStr.substr(SecondPos + 2);
149+
if (HashStr.empty())
150+
continue;
151+
152+
if (!getLinker()->isPostLTOPhase())
153+
continue;
154+
155+
errno = 0;
156+
char *End = nullptr;
157+
unsigned long long Parsed = std::strtoull(HashStr.c_str(), &End, 16);
158+
if (errno != 0 || End == HashStr.c_str() || *End != '\0')
159+
continue;
160+
uint64_t Hash = Parsed;
161+
162+
auto It = FilesByHash.find(Hash);
163+
if (It == FilesByHash.end())
164+
continue;
165+
166+
getLinker()->setSectionName(S, BaseName);
167+
getLinker()->setRuleMatchingInput(S, BitcodeFile(*It->second));
168+
if (TraceLTO) {
169+
(void)getLinker()->reportDiag(
170+
getLinker()->getNoteDiagID(
171+
"Applying section namespace override %0"),
172+
Name);
173+
}
174+
}
175+
}
176+
177+
void ActBeforeSectionMerging() override {
178+
if (!getLinker()->getLinkerScript().hasSectionsCommand() ||
179+
getLinker()->getLinkMode() == LinkerWrapper::PartialLink ||
180+
FilesByHash.empty())
181+
return;
182+
183+
auto ExpSort = getLinker()->sortInputSectionsForSectionMerging(
184+
[&](const Section &A, const Section &B) {
185+
InputFile RMInputA = A.getRuleMatchingInput();
186+
InputFile RMInputB = B.getRuleMatchingInput();
187+
assert(RMInputA.getInputFile() && RMInputB.getInputFile() &&
188+
"rule-matching inputs must be valid");
189+
190+
if (RMInputA.isInternal() && !RMInputB.isInternal())
191+
return false;
192+
if (!RMInputA.isInternal() && RMInputB.isInternal())
193+
return true;
194+
195+
auto getEffectiveOrdinal = [&](const Section &S) -> uint32_t {
196+
InputFile IF = S.getInputFile();
197+
if (IF.isLTOGeneratedObject() && !S.hasOldInputFile()) {
198+
assert(LastBitcodeFile.has_value() &&
199+
"LTO-generated objects require a last bitcode input");
200+
return LastBitcodeFile->getOrdinal();
201+
}
202+
return S.getRuleMatchingInput().getOrdinal();
203+
};
204+
205+
return getEffectiveOrdinal(A) < getEffectiveOrdinal(B);
206+
},
207+
"AdvancedLTO: sort input sections by original input ordinal");
208+
ELDEXP_REPORT_AND_RETURN_VOID_IF_ERROR(getLinker(), ExpSort);
209+
}
210+
211+
private:
212+
ModuleData *importModule(LTOModule &M) {
213+
auto *ID = &M;
214+
auto It = Modules.find(ID);
215+
if (It == Modules.end())
216+
return nullptr;
217+
return It->second.get();
218+
}
219+
220+
Expected<Section> getOrCreateSection(ModuleData &Data,
221+
llvm::StringRef SectionName) {
222+
std::string Key = SectionName.str();
223+
auto It = Data.SectionsByName.find(Key);
224+
if (It != Data.SectionsByName.end())
225+
return It->second;
226+
227+
auto ExpSection = getLinker()->createBitcodeSection(Key, Data.BCFile);
228+
ELDEXP_RETURN_DIAGENTRY_IF_ERROR(ExpSection);
229+
Data.SectionsByName.emplace(std::move(Key), *ExpSection);
230+
return *ExpSection;
231+
}
232+
233+
private:
234+
bool EnableLTOLinkerScripts = false;
235+
bool TraceLTO = false;
236+
std::unordered_map<LTOModule *, std::unique_ptr<ModuleData>> Modules;
237+
std::unordered_map<uint64_t, eld::BitcodeFile *> FilesByHash;
238+
std::optional<BitcodeFile> LastBitcodeFile;
239+
};
240+
241+
} // namespace
242+
243+
ELD_REGISTER_PLUGIN(AdvancedLTO)

Plugins/AdvancedLTO/CMakeLists.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
set(SOURCES AdvancedLTO.cpp)
2+
3+
if(NOT CYGWIN AND LLVM_ENABLE_PIC)
4+
set(SHARED_LIB_SOURCES ${SOURCES})
5+
6+
set(bsl ${BUILD_SHARED_LIBS})
7+
set(BUILD_SHARED_LIBS ON)
8+
9+
add_llvm_library(AdvancedLTO BUILDTREE_ONLY ${SHARED_LIB_SOURCES} LINK_LIBS LW)
10+
11+
set(BUILD_SHARED_LIBS ${bsl})
12+
13+
install(TARGETS AdvancedLTO
14+
COMPONENT ld.eld
15+
LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX}
16+
ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}
17+
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
18+
19+
if(TARGET ld.eld)
20+
add_dependencies(ld.eld AdvancedLTO)
21+
endif()
22+
23+
if(TARGET install-ld.eld)
24+
add_dependencies(install-ld.eld AdvancedLTO)
25+
endif()
26+
27+
if(TARGET check-eld)
28+
add_dependencies(check-eld AdvancedLTO)
29+
endif()
30+
endif()

Plugins/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
add_subdirectory(HelloWorldPlugin)
1+
add_subdirectory(HelloWorldPlugin)
2+
add_subdirectory(AdvancedLTO)

include/eld/Config/GeneralOptions.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,10 @@ class GeneralOptions {
405405

406406
bool hasFatLTOObjects() const { return FatLTOObjects; }
407407

408+
void setLTOLinkerScripts(bool V = true) { LTOLinkerScripts = V; }
409+
410+
bool hasLTOLinkerScripts() const { return LTOLinkerScripts; }
411+
408412
void setLTOOptions(llvm::StringRef OptionType);
409413

410414
void addLTOCodeGenOptions(std::string O);
@@ -1289,7 +1293,8 @@ class GeneralOptions {
12891293
uint32_t GPSize = 8; // -G, --gpsize
12901294
bool Lto = false;
12911295
bool FatLTOObjects = false; // --fat-lto-objects
1292-
bool LTOUseAs = false; // --flto-use-as
1296+
bool LTOLinkerScripts = false; // --lto-linker-scripts
1297+
bool LTOUseAs = false; // -flto-use-as
12931298
StripSymbolMode StripSymbols = KeepAllSymbols; // Strip symbols ?
12941299
bool BPageAlignSegments = true; // Does the linker need to align segments to
12951300
// a page.

include/eld/Core/Module.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ class Module {
335335

336336
void addSymbol(ResolveInfo *R);
337337

338-
LDSymbol *addSymbolFromBitCode(ObjectFile &CurInput, const std::string &Name,
338+
LDSymbol *addSymbolFromBitCode(BitcodeFile &CurInput, const std::string &Name,
339339
ResolveInfo::Type Type, ResolveInfo::Desc Desc,
340340
ResolveInfo::Binding Binding,
341341
ResolveInfo::SizeType Size,

include/eld/Driver/GnuLinkerOptions.td

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,14 @@ defm no_fat_lto_objects
12721272
: mDashDeprWithOpt<"no-fat-lto-objects", "no_fat_lto_objects", "Ignore "
12731273
".llvm.lto section in fat LTO objects">,
12741274
Group<grp_ltooptions>;
1275+
def lto_linker_scripts : Flag<["--"], "lto-linker-scripts">,
1276+
HelpText<"Make LTO respect section mappings "
1277+
"specified in linker scripts">,
1278+
Group<grp_ltooptions>;
1279+
def no_lto_linker_scripts : Flag<["--"], "no-lto-linker-scripts">,
1280+
HelpText<"Ignore section mappings specified in "
1281+
"linker scripts during LTO (default)">,
1282+
Group<grp_ltooptions>;
12751283
defm ffat_lto_objects : mDashDeprAlias<"ffat-lto-objects", "fat_lto_objects",
12761284
"Alias for --fat-lto-objects">,
12771285
Group<grp_ltooptions>;
@@ -1430,6 +1438,16 @@ def : J<"plugin-opt=jobs=">,
14301438
HelpText<"Alias for --thinlto-jobs=">,
14311439
Group<grp_ltooptions>;
14321440

1441+
def thinlto_cache_dir_eq
1442+
: JJ<"thinlto-cache-dir=">,
1443+
HelpText<"Path to ThinLTO cached object file directory">,
1444+
MetaVarName<"<directory>">,
1445+
Group<grp_ltooptions>;
1446+
def : J<"plugin-opt=cache-dir=">,
1447+
Alias<thinlto_cache_dir_eq>,
1448+
HelpText<"Alias for --thinlto-cache-dir=">,
1449+
Group<grp_ltooptions>;
1450+
14331451
def lto_O : JJ<"lto-O">,
14341452
MetaVarName<"<opt-level>">,
14351453
HelpText<"Optimization level for LTO">,

include/eld/Input/BitcodeFile.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ class BitcodeFile : public ObjectFile {
4747
return (I->getKind() == InputFile::BitcodeFileKind);
4848
}
4949

50-
bool createLTOInputFile(const std::string &ModuleID);
50+
bool createLTOInputFile(const std::string &ModuleID,
51+
bool IncludeLocalSymbols = false);
5152

5253
llvm::lto::InputFile &getInputFile() const { return *LTOInputFile; }
5354

@@ -77,6 +78,10 @@ class BitcodeFile : public ObjectFile {
7778

7879
Section *getInputSectionForSymbol(const ResolveInfo &) const;
7980

81+
void setResolveInfoForLTOSymbol(unsigned Index, ResolveInfo &Info);
82+
83+
ResolveInfo *getResolveInfoForLTOSymbol(unsigned Index) const;
84+
8085
private:
8186
void inferObjectInfo();
8287

@@ -93,6 +98,7 @@ class BitcodeFile : public ObjectFile {
9398
// Marked by comdat index in Module if accepted: (true if not rejected)
9499
llvm::DenseMap<int, bool> BCComdats;
95100
std::unordered_map<const ResolveInfo *, Section *> InputSectionForSymbol;
101+
std::vector<ResolveInfo *> ResolveInfoForLTOSymbol;
96102
plugin::LTOModule *PluginModule;
97103
};
98104

include/eld/PluginAPI/LinkerWrapper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class DLL_A_EXPORT LinkerWrapper {
175175
plugin::Symbol::Binding Binding, Section InputSection,
176176
plugin::Symbol::Kind Kind,
177177
plugin::Symbol::Visibility Visibility = plugin::Symbol::Default,
178-
unsigned Type = 0, uint64_t Size = 0);
178+
unsigned Type = 0, uint64_t Size = 0, unsigned SymbolIndex = 0);
179179

180180
/// Override the output section to OutputSection for Section S.
181181
/// \param S Section for which the output section needs to be overriden.

include/eld/PluginAPI/PluginADT.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,7 @@ struct DLL_A_EXPORT LinkerConfig {
14641464
bool hasBSymbolic() const;
14651465
bool hasGCSections() const;
14661466
bool hasUniqueOutputSections() const;
1467+
bool hasLTOLinkerScripts() const;
14671468

14681469
/// Return options set by --flto-options.
14691470
const std::vector<std::string> &getLTOOptions() const;

0 commit comments

Comments
 (0)