LLVM 23.0.0git
COFF_x86_64.cpp
Go to the documentation of this file.
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
15#include "JITLinkGeneric.h"
16#include "SEHFrameSupport.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 {
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, 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())
60 RelSect, this, &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(Rel);
70 auto SymbolIt = Rel.getSymbol();
71 if (SymbolIt == getObject().symbol_end()) {
73 formatv("Invalid symbol index in relocation entry. "
74 "index: {0}, section: {1}",
75 COFFRel->SymbolTableIndex, FixupSect.getIndex()),
77 }
78
79 object::COFFSymbolRef COFFSymbol = getObject().getCOFFSymbol(*SymbolIt);
80 COFFSymbolIndex SymIndex = getObject().getSymbolIndex(COFFSymbol);
81
82 Symbol *GraphSymbol = getGraphSymbol(SymIndex);
83 if (!GraphSymbol)
85 formatv("Could not find symbol at given index, did you add it to "
86 "JITSymbolTable? index: {0}, section: {1}",
87 SymIndex, FixupSect.getIndex()),
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()) {
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 }
108 Kind = EdgeKind_coff_x86_64::PCRel32;
109 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
110 break;
111 }
113 Kind = EdgeKind_coff_x86_64::PCRel32;
114 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
115 Addend -= 1;
116 break;
117 }
119 Kind = EdgeKind_coff_x86_64::PCRel32;
120 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
121 Addend -= 2;
122 break;
123 }
125 Kind = EdgeKind_coff_x86_64::PCRel32;
126 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
127 Addend -= 3;
128 break;
129 }
131 Kind = EdgeKind_coff_x86_64::PCRel32;
132 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
133 Addend -= 4;
134 break;
135 }
137 Kind = EdgeKind_coff_x86_64::PCRel32;
138 Addend = *reinterpret_cast<const support::little32_t *>(FixupPtr);
139 Addend -= 5;
140 break;
141 }
143 Kind = EdgeKind_coff_x86_64::Pointer64;
144 Addend = *reinterpret_cast<const support::little64_t *>(FixupPtr);
145 break;
146 }
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 "secidx", orc::ExecutorAddr(SectionIdx), 2, Linkage::Strong,
158 Scope::Local, false);
159 GraphSymbol = AbsSym;
160 break;
161 }
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>("Unsupported x86_64 relocation:" +
172 formatv("{0:d}", 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(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),
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(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(&Sec);
240 if (Inserted) {
241 SectionRange Range(Sec);
242 It->second = Range.getStart();
243 }
244 return It->second;
245 }
246
247 GetImageBaseSymbol GetImageBase;
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).
289 for (auto *Sym : G.external_symbols())
290 if (Sym->hasName() && (*Sym->getName()).starts_with(ImpPrefix))
291 Imps.push_back(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("$__IAT", orc::MemProt::Read);
304
305 for (auto *Imp : Imps) {
307 G.intern((*Imp->getName()).drop_front(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(std::move(Base), 0,
313 /*IsWeaklyReferenced=*/false);
314
315 // 8-byte slot holding &X, with __imp_X defined over it.
317 G.makeDefined(*Imp, Slot.getBlock(), 0, G.getPointerSize(), Linkage::Strong,
318 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(R);
343 }
344}
345
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(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(std::move(MarkLive));
375 Config.PrePrunePasses.push_back(SEHFrameKeepAlivePass(".pdata"));
376 } else
377 Config.PrePrunePasses.push_back(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(synthesizeIATEntries_COFF_x86_64);
383
384 // Add COFF edge lowering passes.
385 Config.PreFixupPasses.push_back(COFFLinkGraphLowering_x86_64());
386 }
387
388 if (auto Err = Ctx->modifyPassConfig(*G, Config))
389 return Ctx->notifyFailed(std::move(Err));
390
391 COFFJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));
392}
393
394} // namespace jitlink
395} // namespace llvm
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
bbsections Prepares for basic block sections
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static Error getObject(const T *&Obj, MemoryBufferRef M, const void *Ptr, const uint64_t Size=sizeof(T))
#define G(x, y, z)
Definition MD5.cpp:55
#define T
ConstantRange Range(APInt(BitWidth, Low), APInt(BitWidth, High))
#define LLVM_DEBUG(...)
Definition Debug.h:119
const T * data() const
Definition ArrayRef.h:138
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
StringRef getBufferIdentifier() const
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr size_t size() const
Get the string size.
Definition StringRef.h:144
Manages the enabling and disabling of subtarget specific features.
Target - Wrapper for Target specific information.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:47
static Expected< std::unique_ptr< COFFObjectFile > > createCOFFObjectFile(MemoryBufferRef Object)
This is a value type class that represents a single relocation in the list of relocations in the obje...
Definition ObjectFile.h:54
uint64_t getType() const
Definition ObjectFile.h:633
uint64_t getOffset() const
Definition ObjectFile.h:625
symbol_iterator getSymbol() const
Definition ObjectFile.h:629
This is a value type class that represents a single section in the list of sections in the object fil...
Definition ObjectFile.h:83
uint64_t getIndex() const
Definition ObjectFile.h:530
uint64_t getAddress() const
Definition ObjectFile.h:526
Represents an address in the executor process.
Pointer to a pooled string representing a symbol name.
@ IMAGE_REL_AMD64_REL32
Definition COFF.h:365
@ IMAGE_REL_AMD64_REL32_5
Definition COFF.h:370
@ IMAGE_REL_AMD64_ADDR64
Definition COFF.h:362
@ IMAGE_REL_AMD64_REL32_3
Definition COFF.h:368
@ IMAGE_REL_AMD64_ADDR32NB
Definition COFF.h:364
@ IMAGE_REL_AMD64_SECTION
Definition COFF.h:371
@ IMAGE_REL_AMD64_REL32_2
Definition COFF.h:367
@ IMAGE_REL_AMD64_REL32_1
Definition COFF.h:366
@ IMAGE_REL_AMD64_SECREL
Definition COFF.h:372
@ IMAGE_REL_AMD64_REL32_4
Definition COFF.h:369
detail::packed_endian_specific_integral< int16_t, llvm::endianness::little, unaligned > little16_t
Definition Endian.h:297
detail::packed_endian_specific_integral< int32_t, llvm::endianness::little, unaligned > little32_t
Definition Endian.h:300
detail::packed_endian_specific_integral< int64_t, llvm::endianness::little, unaligned > little64_t
Definition Endian.h:303
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:558
LLVM_ABI std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition Error.cpp:94
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
support::ulittle32_t SymbolTableIndex
Definition COFF.h:485