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 | |
21 | namespace llvm { |
22 | namespace bolt { |
23 | |
24 | namespace { |
25 | |
26 | bool 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. |
34 | Error 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 | |
57 | void 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 | |
76 | struct 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 | |
184 | JITLinkLinker::JITLinkLinker(BinaryContext &BC, |
185 | std::unique_ptr<ExecutableFileMemoryManager> MM) |
186 | : BC(BC), MM(std::move(MM)) {} |
187 | |
188 | JITLinkLinker::~JITLinkLinker() { cantFail(Err: MM->deallocate(Allocs: std::move(Allocs))); } |
189 | |
190 | void 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 | |
209 | std::optional<JITLinkLinker::SymbolInfo> |
210 | JITLinkLinker::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 | |
218 | SmallVector<jitlink::Block *, 2> |
219 | JITLinkLinker::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 | |
227 | size_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 | |