1//===- bolt/Rewrite/JITLinkLinker.cpp - BOLTLinker using JITLink ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "bolt/Rewrite/JITLinkLinker.h"
10#include "bolt/Core/BinaryContext.h"
11#include "bolt/Core/BinaryData.h"
12#include "bolt/Core/BinarySection.h"
13#include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
14#include "llvm/ExecutionEngine/JITLink/JITLink.h"
15#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
16#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
17#include "llvm/Support/Debug.h"
18
19#define DEBUG_TYPE "bolt"
20
21namespace llvm {
22namespace bolt {
23
24namespace {
25
26bool hasSymbols(const jitlink::Block &B) {
27 return llvm::any_of(Range: B.getSection().symbols(),
28 P: [&B](const auto &S) { return &S->getBlock() == &B; });
29}
30
31/// Liveness in JITLink is based on symbols so sections that do not contain
32/// any symbols will always be pruned. This pass adds anonymous symbols to
33/// needed sections to prevent pruning.
34Error markSectionsLive(jitlink::LinkGraph &G) {
35 for (auto &Section : G.sections()) {
36 // We only need allocatable sections.
37 if (Section.getMemLifetime() == orc::MemLifetime::NoAlloc)
38 continue;
39
40 // Skip empty sections.
41 if (JITLinkLinker::sectionSize(Section) == 0)
42 continue;
43
44 for (auto *Block : Section.blocks()) {
45 // No need to add symbols if it already has some.
46 if (hasSymbols(B: *Block))
47 continue;
48
49 G.addAnonymousSymbol(Content&: *Block, /*Offset=*/0, /*Size=*/0,
50 /*IsCallable=*/false, /*IsLive=*/true);
51 }
52 }
53
54 return jitlink::markAllSymbolsLive(G);
55}
56
57void reassignSectionAddress(jitlink::LinkGraph &LG,
58 const BinarySection &BinSection, uint64_t Address) {
59 auto *JLSection = LG.findSectionByName(Name: BinSection.getSectionID());
60 assert(JLSection && "cannot find section in LinkGraph");
61
62 auto BlockAddress = Address;
63 for (auto *Block : JITLinkLinker::orderedBlocks(Section: *JLSection)) {
64 // FIXME it would seem to make sense to align here. However, in
65 // non-relocation mode, we simply use the original address of functions
66 // which might not be aligned with the minimum alignment used by
67 // BinaryFunction (2). Example failing test when aligning:
68 // bolt/test/X86/addr32.s
69 Block->setAddress(orc::ExecutorAddr(BlockAddress));
70 BlockAddress += Block->getSize();
71 }
72}
73
74} // anonymous namespace
75
76struct JITLinkLinker::Context : jitlink::JITLinkContext {
77 JITLinkLinker &Linker;
78 JITLinkLinker::SectionsMapper MapSections;
79
80 Context(JITLinkLinker &Linker, JITLinkLinker::SectionsMapper MapSections)
81 : JITLinkContext(&Linker.Dylib), Linker(Linker),
82 MapSections(MapSections) {}
83
84 jitlink::JITLinkMemoryManager &getMemoryManager() override {
85 return *Linker.MM;
86 }
87
88 bool shouldAddDefaultTargetPasses(const Triple &TT) const override {
89 // The default passes manipulate DWARF sections in a way incompatible with
90 // BOLT.
91 // TODO check if we can actually use these passes to remove some of the
92 // DWARF manipulation done in BOLT.
93 return false;
94 }
95
96 Error modifyPassConfig(jitlink::LinkGraph &G,
97 jitlink::PassConfiguration &Config) override {
98 Config.PrePrunePasses.push_back(x: markSectionsLive);
99 Config.PostAllocationPasses.push_back(x: [this](auto &G) {
100 MapSections([&G](const BinarySection &Section, uint64_t Address) {
101 reassignSectionAddress(G, Section, Address);
102 });
103 return Error::success();
104 });
105
106 if (G.getTargetTriple().isRISCV()) {
107 Config.PostAllocationPasses.push_back(
108 x: jitlink::createRelaxationPass_ELF_riscv());
109 }
110
111 return Error::success();
112 }
113
114 void notifyFailed(Error Err) override {
115 errs() << "BOLT-ERROR: JITLink failed: " << Err << '\n';
116 exit(status: 1);
117 }
118
119 void
120 lookup(const LookupMap &Symbols,
121 std::unique_ptr<jitlink::JITLinkAsyncLookupContinuation> LC) override {
122 jitlink::AsyncLookupResult AllResults;
123
124 for (const auto &Symbol : Symbols) {
125 std::string SymName = Symbol.first.str();
126 LLVM_DEBUG(dbgs() << "BOLT: looking for " << SymName << "\n");
127
128 if (auto Address = Linker.lookupSymbol(Name: SymName)) {
129 LLVM_DEBUG(dbgs() << "Resolved to address 0x"
130 << Twine::utohexstr(*Address) << "\n");
131 AllResults[Symbol.first] = orc::ExecutorSymbolDef(
132 orc::ExecutorAddr(*Address), JITSymbolFlags());
133 continue;
134 }
135
136 if (const BinaryData *I = Linker.BC.getBinaryDataByName(Name: SymName)) {
137 uint64_t Address = I->isMoved() && !I->isJumpTable()
138 ? I->getOutputAddress()
139 : I->getAddress();
140 LLVM_DEBUG(dbgs() << "Resolved to address 0x"
141 << Twine::utohexstr(Address) << "\n");
142 AllResults[Symbol.first] = orc::ExecutorSymbolDef(
143 orc::ExecutorAddr(Address), JITSymbolFlags());
144 continue;
145 }
146
147 if (Linker.BC.isGOTSymbol(SymName)) {
148 if (const BinaryData *I = Linker.BC.getGOTSymbol()) {
149 uint64_t Address =
150 I->isMoved() ? I->getOutputAddress() : I->getAddress();
151 LLVM_DEBUG(dbgs() << "Resolved to address 0x"
152 << Twine::utohexstr(Address) << "\n");
153 AllResults[Symbol.first] = orc::ExecutorSymbolDef(
154 orc::ExecutorAddr(Address), JITSymbolFlags());
155 continue;
156 }
157 }
158
159 LLVM_DEBUG(dbgs() << "Resolved to address 0x0\n");
160 AllResults[Symbol.first] =
161 orc::ExecutorSymbolDef(orc::ExecutorAddr(0), JITSymbolFlags());
162 }
163
164 LC->run(LR: std::move(AllResults));
165 }
166
167 Error notifyResolved(jitlink::LinkGraph &G) override {
168 for (auto *Symbol : G.defined_symbols()) {
169 SymbolInfo Info{.Address: Symbol->getAddress().getValue(), .Size: Symbol->getSize()};
170 Linker.Symtab.insert(KV: {Symbol->getName().str(), Info});
171 }
172
173 return Error::success();
174 }
175
176 void notifyFinalized(
177 jitlink::JITLinkMemoryManager::FinalizedAlloc Alloc) override {
178 if (Alloc)
179 Linker.Allocs.push_back(x: std::move(Alloc));
180 ++Linker.MM->ObjectsLoaded;
181 }
182};
183
184JITLinkLinker::JITLinkLinker(BinaryContext &BC,
185 std::unique_ptr<ExecutableFileMemoryManager> MM)
186 : BC(BC), MM(std::move(MM)) {}
187
188JITLinkLinker::~JITLinkLinker() { cantFail(Err: MM->deallocate(Allocs: std::move(Allocs))); }
189
190void JITLinkLinker::loadObject(MemoryBufferRef Obj,
191 SectionsMapper MapSections) {
192 auto LG = jitlink::createLinkGraphFromObject(ObjectBuffer: Obj);
193 if (auto E = LG.takeError()) {
194 errs() << "BOLT-ERROR: JITLink failed: " << E << '\n';
195 exit(status: 1);
196 }
197
198 if ((*LG)->getTargetTriple().getArch() != BC.TheTriple->getArch()) {
199 errs() << "BOLT-ERROR: linking object with arch "
200 << (*LG)->getTargetTriple().getArchName()
201 << " into context with arch " << BC.TheTriple->getArchName() << "\n";
202 exit(status: 1);
203 }
204
205 auto Ctx = std::make_unique<Context>(args&: *this, args&: MapSections);
206 jitlink::link(G: std::move(*LG), Ctx: std::move(Ctx));
207}
208
209std::optional<JITLinkLinker::SymbolInfo>
210JITLinkLinker::lookupSymbolInfo(StringRef Name) const {
211 auto It = Symtab.find(Key: Name.data());
212 if (It == Symtab.end())
213 return std::nullopt;
214
215 return It->second;
216}
217
218SmallVector<jitlink::Block *, 2>
219JITLinkLinker::orderedBlocks(const jitlink::Section &Section) {
220 SmallVector<jitlink::Block *, 2> Blocks(Section.blocks());
221 llvm::sort(C&: Blocks, Comp: [](const auto *LHS, const auto *RHS) {
222 return LHS->getAddress() < RHS->getAddress();
223 });
224 return Blocks;
225}
226
227size_t JITLinkLinker::sectionSize(const jitlink::Section &Section) {
228 size_t Size = 0;
229
230 for (const auto *Block : orderedBlocks(Section)) {
231 Size = jitlink::alignToBlock(Addr: Size, B: *Block);
232 Size += Block->getSize();
233 }
234
235 return Size;
236}
237
238} // namespace bolt
239} // namespace llvm
240

source code of bolt/lib/Rewrite/JITLinkLinker.cpp