1//===----- COFF_x86_64.cpp - JIT linker implementation for COFF/x86_64 ----===//
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// COFF/x86_64 jit-link implementation.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/ExecutionEngine/JITLink/COFF_x86_64.h"
14#include "COFFLinkGraphBuilder.h"
15#include "JITLinkGeneric.h"
16#include "SEHFrameSupport.h"
17#include "llvm/BinaryFormat/COFF.h"
18#include "llvm/ExecutionEngine/JITLink/COFF.h"
19#include "llvm/ExecutionEngine/JITLink/x86_64.h"
20#include "llvm/Object/COFF.h"
21#include "llvm/Support/Endian.h"
22
23#define DEBUG_TYPE "jitlink"
24
25using namespace llvm;
26using namespace llvm::jitlink;
27
28namespace {
29
30enum EdgeKind_coff_x86_64 : Edge::Kind {
31 PCRel32 = x86_64::FirstPlatformRelocation,
32 Pointer32NB,
33 Pointer64,
34 SectionIdx16,
35 SecRel32,
36};
37
38class COFFJITLinker_x86_64 : public JITLinker<COFFJITLinker_x86_64> {
39 friend class JITLinker<COFFJITLinker_x86_64>;
40
41public:
42 COFFJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
43 std::unique_ptr<LinkGraph> G,
44 PassConfiguration PassConfig)
45 : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
46
47private:
48 Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
49 return x86_64::applyFixup(G, B, E, GOTSymbol: nullptr);
50 }
51};
52
53class COFFLinkGraphBuilder_x86_64 : public COFFLinkGraphBuilder {
54private:
55 Error addRelocations() override {
56 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
57
58 for (const auto &RelSect : sections())
59 if (Error Err = COFFLinkGraphBuilder::forEachRelocation(
60 RelSec: RelSect, Instance: this, Method: &COFFLinkGraphBuilder_x86_64::addSingleRelocation))
61 return Err;
62
63 return Error::success();
64 }
65
66 Error addSingleRelocation(const object::RelocationRef &Rel,
67 const object::SectionRef &FixupSect,
68 Block &BlockToFix) {
69 const object::coff_relocation *COFFRel = getObject().getCOFFRelocation(Reloc: Rel);
70 auto SymbolIt = Rel.getSymbol();
71 if (SymbolIt == getObject().symbol_end()) {
72 return make_error<StringError>(
73 Args: formatv(Fmt: "Invalid symbol index in relocation entry. "
74 "index: {0}, section: {1}",
75 Vals: COFFRel->SymbolTableIndex, Vals: FixupSect.getIndex()),
76 Args: inconvertibleErrorCode());
77 }
78
79 object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(Symbol: *SymbolIt);
80 COFFSymbolIndex SymIndex = getObject().getSymbolIndex(Symbol: COFFSymbol);
81
82 Symbol *GraphSymbol = getGraphSymbol(SymIndex);
83 if (!GraphSymbol)
84 return make_error<StringError>(
85 Args: formatv(Fmt: "Could not find symbol at given index, did you add it to "
86 "JITSymbolTable? index: {0}, section: {1}",
87 Vals&: SymIndex, Vals: FixupSect.getIndex()),
88 Args: inconvertibleErrorCode());
89
90 int64_t Addend = 0;
91 orc::ExecutorAddr FixupAddress =
92 orc::ExecutorAddr(FixupSect.getAddress()) + Rel.getOffset();
93 Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
94
95 Edge::Kind Kind = Edge::Invalid;
96 const char *FixupPtr = BlockToFix.getContent().data() + Offset;
97 Symbol *ImageBase = GetImageBaseSymbol()(getGraph());
98
99 switch (Rel.getType()) {
100 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR32NB: {
101 if (!ImageBase)
102 ImageBase = &addImageBaseSymbol();
103 Kind = EdgeKind_coff_x86_64::Pointer32NB;
104 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
105 break;
106 }
107 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32: {
108 Kind = EdgeKind_coff_x86_64::PCRel32;
109 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
110 break;
111 }
112 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_1: {
113 Kind = EdgeKind_coff_x86_64::PCRel32;
114 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
115 Addend -= 1;
116 break;
117 }
118 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_2: {
119 Kind = EdgeKind_coff_x86_64::PCRel32;
120 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
121 Addend -= 2;
122 break;
123 }
124 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_3: {
125 Kind = EdgeKind_coff_x86_64::PCRel32;
126 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
127 Addend -= 3;
128 break;
129 }
130 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_4: {
131 Kind = EdgeKind_coff_x86_64::PCRel32;
132 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
133 Addend -= 4;
134 break;
135 }
136 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_REL32_5: {
137 Kind = EdgeKind_coff_x86_64::PCRel32;
138 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
139 Addend -= 5;
140 break;
141 }
142 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_ADDR64: {
143 Kind = EdgeKind_coff_x86_64::Pointer64;
144 Addend = *reinterpret_cast<const support::little64_t *>(FixupPtr);
145 break;
146 }
147 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECTION: {
148 Kind = EdgeKind_coff_x86_64::SectionIdx16;
149 Addend = *reinterpret_cast<const support::little16_t *>(FixupPtr);
150 uint64_t SectionIdx = 0;
151 if (COFFSymbol.isAbsolute())
152 SectionIdx = getObject().getNumberOfSections() + 1;
153 else
154 SectionIdx = COFFSymbol.getSectionNumber();
155
156 auto *AbsSym = &getGraph().addAbsoluteSymbol(
157 Name: "secidx", Address: orc::ExecutorAddr(SectionIdx), Size: 2, L: Linkage::Strong,
158 S: Scope::Local, IsLive: false);
159 GraphSymbol = AbsSym;
160 break;
161 }
162 case COFF::RelocationTypeAMD64::IMAGE_REL_AMD64_SECREL: {
163 // FIXME: SECREL to external symbol should be handled
164 if (!GraphSymbol->isDefined())
165 return Error::success();
166 Kind = EdgeKind_coff_x86_64::SecRel32;
167 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
168 break;
169 }
170 default: {
171 return make_error<JITLinkError>(Args: "Unsupported x86_64 relocation:" +
172 formatv(Fmt: "{0:d}", Vals: Rel.getType()));
173 }
174 };
175
176 Edge GE(Kind, Offset, *GraphSymbol, Addend);
177 LLVM_DEBUG({
178 dbgs() << " ";
179 printEdge(dbgs(), BlockToFix, GE, getCOFFX86RelocationKindName(Kind));
180 dbgs() << "\n";
181 });
182
183 BlockToFix.addEdge(E: std::move(GE));
184
185 return Error::success();
186 }
187
188public:
189 COFFLinkGraphBuilder_x86_64(const object::COFFObjectFile &Obj,
190 std::shared_ptr<orc::SymbolStringPool> SSP,
191 const Triple T, const SubtargetFeatures Features)
192 : COFFLinkGraphBuilder(Obj, std::move(SSP), std::move(T),
193 std::move(Features),
194 getCOFFX86RelocationKindName) {}
195};
196
197class COFFLinkGraphLowering_x86_64 {
198public:
199 // Lowers COFF x86_64 specific edges to generic x86_64 edges.
200 Error operator()(LinkGraph &G) {
201 for (auto *B : G.blocks()) {
202 for (auto &E : B->edges()) {
203 switch (E.getKind()) {
204 case EdgeKind_coff_x86_64::Pointer32NB: {
205 auto ImageBase = GetImageBase(G);
206 assert(ImageBase && "__ImageBase symbol must be defined");
207 E.setAddend(E.getAddend() - ImageBase->getAddress().getValue());
208 E.setKind(x86_64::Pointer32);
209 break;
210 }
211 case EdgeKind_coff_x86_64::PCRel32: {
212 E.setKind(x86_64::PCRel32);
213 break;
214 }
215 case EdgeKind_coff_x86_64::Pointer64: {
216 E.setKind(x86_64::Pointer64);
217 break;
218 }
219 case EdgeKind_coff_x86_64::SectionIdx16: {
220 E.setKind(x86_64::Pointer16);
221 break;
222 }
223 case EdgeKind_coff_x86_64::SecRel32: {
224 E.setAddend(E.getAddend() -
225 getSectionStart(Sec&: E.getTarget().getSection()).getValue());
226 E.setKind(x86_64::Pointer32);
227 break;
228 }
229 default:
230 break;
231 }
232 }
233 }
234 return Error::success();
235 }
236
237private:
238 orc::ExecutorAddr getSectionStart(Section &Sec) {
239 auto [It, Inserted] = SectionStartCache.try_emplace(Key: &Sec);
240 if (Inserted) {
241 SectionRange Range(Sec);
242 It->second = Range.getStart();
243 }
244 return It->second;
245 }
246
247 GetImageBaseSymbol GetImageBase;
248 DenseMap<Section *, orc::ExecutorAddr> SectionStartCache;
249};
250
251// Synthesize COFF __imp_ Import Address Table (IAT) entries.
252//
253// For a dllimport reference, codegen emits an indirect access through a named
254// __imp_X symbol, e.g.
255//
256// callq *__imp_bar(%rip) ; or, for data: movq __imp_g(%rip), %rax
257//
258// where __imp_X is an undefined external. This pass supplies the missing IAT
259// entry by defining __imp_X over an 8-byte pointer slot that holds X's address:
260//
261// __imp_bar:
262// .quad bar ; X is resolved as an ordinary external
263//
264// X is left external, so its address is provided by whatever resolves the
265// JITDylib's externals (an import library, a DynamicLibrarySearchGenerator,
266// AutoImportGenerator, ...). If X is unresolvable the link fails, exactly as a
267// static link against the corresponding import library would.
268//
269// This is the COFF analog of the ELF/Mach-O GOT builder, but deliberately NOT
270// written as a TableManager/visitEdge pass like x86_64::GOTTableManager. ELF's
271// GOT references are *nameless* edge kinds, so that builder has to create an
272// anonymous entry and redirect every edge to it (and, for our case, would then
273// have to delete the now-orphaned __imp_X external so it isn't looked up).
274// COFF instead references a *named* __imp_X symbol, so the simpler and more
275// natural thing is to define that symbol over the slot: edges to __imp_X then
276// resolve to it with no edge rewriting and no orphan cleanup, call and
277// data-access references are handled identically, and sharing is automatic
278// because there is exactly one __imp_X symbol per import.
279//
280// Direct (non-dllimport) references such as `callq foo` are intentionally not
281// handled here: those are either kept in range by the slab allocator or thunked
282// by the opt-in AutoImportGenerator -- both outside this pass.
283Error synthesizeIATEntries_COFF_x86_64(LinkGraph &G) {
284 static constexpr StringRef ImpPrefix = "__imp_";
285
286 // Collect the external __imp_ symbols up front: we mutate the symbol lists
287 // below (makeDefined / addExternalSymbol).
288 SmallVector<Symbol *, 8> Imps;
289 for (auto *Sym : G.external_symbols())
290 if (Sym->hasName() && (*Sym->getName()).starts_with(Prefix: ImpPrefix))
291 Imps.push_back(Elt: Sym);
292 if (Imps.empty())
293 return Error::success();
294
295 auto FindByName = [&](const orc::SymbolStringPtr &Name) -> Symbol * {
296 if (auto *Sym = G.findExternalSymbolByName(Name))
297 return Sym;
298 if (auto *Sym = G.findDefinedSymbolByName(Name))
299 return Sym;
300 return nullptr;
301 };
302
303 Section &IATSec = G.createSection(Name: "$__IAT", Prot: orc::MemProt::Read);
304
305 for (auto *Imp : Imps) {
306 orc::SymbolStringPtr Base =
307 G.intern(SymbolName: (*Imp->getName()).drop_front(N: ImpPrefix.size()));
308
309 // Find the real target X, or add it as an external to be resolved normally.
310 Symbol *Target = FindByName(std::move(Base));
311 if (!Target)
312 Target = &G.addExternalSymbol(Name: std::move(Base), Size: 0,
313 /*IsWeaklyReferenced=*/false);
314
315 // 8-byte slot holding &X, with __imp_X defined over it.
316 Symbol &Slot = x86_64::createAnonymousPointer(G, PointerSection&: IATSec, InitialTarget: Target);
317 G.makeDefined(Sym&: *Imp, Content&: Slot.getBlock(), Offset: 0, Size: G.getPointerSize(), L: Linkage::Strong,
318 S: Scope::Local, /*IsLive=*/true);
319 }
320
321 return Error::success();
322}
323} // namespace
324
325namespace llvm {
326namespace jitlink {
327
328/// Return the string name of the given COFF x86_64 edge kind.
329const char *getCOFFX86RelocationKindName(Edge::Kind R) {
330 switch (R) {
331 case PCRel32:
332 return "PCRel32";
333 case Pointer32NB:
334 return "Pointer32NB";
335 case Pointer64:
336 return "Pointer64";
337 case SectionIdx16:
338 return "SectionIdx16";
339 case SecRel32:
340 return "SecRel32";
341 default:
342 return x86_64::getEdgeKindName(K: R);
343 }
344}
345
346Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromCOFFObject_x86_64(
347 MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
348 LLVM_DEBUG({
349 dbgs() << "Building jitlink graph for new input "
350 << ObjectBuffer.getBufferIdentifier() << "...\n";
351 });
352
353 auto COFFObj = object::ObjectFile::createCOFFObjectFile(Object: ObjectBuffer);
354 if (!COFFObj)
355 return COFFObj.takeError();
356
357 auto Features = (*COFFObj)->getFeatures();
358 if (!Features)
359 return Features.takeError();
360
361 return COFFLinkGraphBuilder_x86_64(**COFFObj, std::move(SSP),
362 (*COFFObj)->makeTriple(),
363 std::move(*Features))
364 .buildGraph();
365}
366
367void link_COFF_x86_64(std::unique_ptr<LinkGraph> G,
368 std::unique_ptr<JITLinkContext> Ctx) {
369 PassConfiguration Config;
370 const Triple &TT = G->getTargetTriple();
371 if (Ctx->shouldAddDefaultTargetPasses(TT)) {
372 // Add a mark-live pass.
373 if (auto MarkLive = Ctx->getMarkLivePass(TT)) {
374 Config.PrePrunePasses.push_back(x: std::move(MarkLive));
375 Config.PrePrunePasses.push_back(x: SEHFrameKeepAlivePass(".pdata"));
376 } else
377 Config.PrePrunePasses.push_back(x: markAllSymbolsLive);
378
379 // Synthesize __imp_X IAT entries for dllimport references, like the GOT/PLT
380 // builders for ELF/Mach-O. Runs in PostPrune (before external-symbol
381 // lookup) so the X targets it introduces are resolved normally.
382 Config.PostPrunePasses.push_back(x: synthesizeIATEntries_COFF_x86_64);
383
384 // Add COFF edge lowering passes.
385 Config.PreFixupPasses.push_back(x: COFFLinkGraphLowering_x86_64());
386 }
387
388 if (auto Err = Ctx->modifyPassConfig(G&: *G, Config))
389 return Ctx->notifyFailed(Err: std::move(Err));
390
391 COFFJITLinker_x86_64::link(Args: std::move(Ctx), Args: std::move(G), Args: std::move(Config));
392}
393
394} // namespace jitlink
395} // namespace llvm
396