LLVM 20.0.0git
MachO_x86_64.cpp
Go to the documentation of this file.
1//===---- MachO_x86_64.cpp -JIT linker implementation for MachO/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// MachO/x86-64 jit-link implementation.
10//
11//===----------------------------------------------------------------------===//
12
16
19
20#define DEBUG_TYPE "jitlink"
21
22using namespace llvm;
23using namespace llvm::jitlink;
24
25namespace {
26
27class MachOLinkGraphBuilder_x86_64 : public MachOLinkGraphBuilder {
28public:
29 MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile &Obj,
30 std::shared_ptr<orc::SymbolStringPool> SSP,
31 SubtargetFeatures Features)
32 : MachOLinkGraphBuilder(Obj, std::move(SSP),
33 Triple("x86_64-apple-darwin"),
34 std::move(Features), x86_64::getEdgeKindName) {}
35
36private:
37 enum MachONormalizedRelocationType : unsigned {
38 MachOBranch32,
39 MachOPointer32,
40 MachOPointer64,
41 MachOPointer64Anon,
42 MachOPCRel32,
43 MachOPCRel32Minus1,
44 MachOPCRel32Minus2,
45 MachOPCRel32Minus4,
46 MachOPCRel32Anon,
47 MachOPCRel32Minus1Anon,
48 MachOPCRel32Minus2Anon,
49 MachOPCRel32Minus4Anon,
50 MachOPCRel32GOTLoad,
51 MachOPCRel32GOT,
52 MachOPCRel32TLV,
53 MachOSubtractor32,
54 MachOSubtractor64,
55 };
56
58 getRelocKind(const MachO::relocation_info &RI) {
59 switch (RI.r_type) {
61 if (!RI.r_pcrel) {
62 if (RI.r_length == 3)
63 return RI.r_extern ? MachOPointer64 : MachOPointer64Anon;
64 else if (RI.r_extern && RI.r_length == 2)
65 return MachOPointer32;
66 }
67 break;
69 if (RI.r_pcrel && RI.r_length == 2)
70 return RI.r_extern ? MachOPCRel32 : MachOPCRel32Anon;
71 break;
73 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
74 return MachOBranch32;
75 break;
77 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
78 return MachOPCRel32GOTLoad;
79 break;
81 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
82 return MachOPCRel32GOT;
83 break;
85 if (!RI.r_pcrel && RI.r_extern) {
86 if (RI.r_length == 2)
87 return MachOSubtractor32;
88 else if (RI.r_length == 3)
89 return MachOSubtractor64;
90 }
91 break;
93 if (RI.r_pcrel && RI.r_length == 2)
94 return RI.r_extern ? MachOPCRel32Minus1 : MachOPCRel32Minus1Anon;
95 break;
97 if (RI.r_pcrel && RI.r_length == 2)
98 return RI.r_extern ? MachOPCRel32Minus2 : MachOPCRel32Minus2Anon;
99 break;
101 if (RI.r_pcrel && RI.r_length == 2)
102 return RI.r_extern ? MachOPCRel32Minus4 : MachOPCRel32Minus4Anon;
103 break;
105 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
106 return MachOPCRel32TLV;
107 break;
108 }
109
110 return make_error<JITLinkError>(
111 "Unsupported x86-64 relocation: address=" +
112 formatv("{0:x8}", RI.r_address) +
113 ", symbolnum=" + formatv("{0:x6}", RI.r_symbolnum) +
114 ", kind=" + formatv("{0:x1}", RI.r_type) +
115 ", pc_rel=" + (RI.r_pcrel ? "true" : "false") +
116 ", extern=" + (RI.r_extern ? "true" : "false") +
117 ", length=" + formatv("{0:d}", RI.r_length));
118 }
119
120 using PairRelocInfo = std::tuple<Edge::Kind, Symbol *, uint64_t>;
121
122 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
123 // returns the edge kind and addend to be used.
124 Expected<PairRelocInfo> parsePairRelocation(
125 Block &BlockToFix, MachONormalizedRelocationType SubtractorKind,
126 const MachO::relocation_info &SubRI, orc::ExecutorAddr FixupAddress,
127 const char *FixupContent, object::relocation_iterator &UnsignedRelItr,
129 using namespace support;
130
131 assert(((SubtractorKind == MachOSubtractor32 && SubRI.r_length == 2) ||
132 (SubtractorKind == MachOSubtractor64 && SubRI.r_length == 3)) &&
133 "Subtractor kind should match length");
134 assert(SubRI.r_extern && "SUBTRACTOR reloc symbol should be extern");
135 assert(!SubRI.r_pcrel && "SUBTRACTOR reloc should not be PCRel");
136
137 if (UnsignedRelItr == RelEnd)
138 return make_error<JITLinkError>("x86_64 SUBTRACTOR without paired "
139 "UNSIGNED relocation");
140
141 auto UnsignedRI = getRelocationInfo(UnsignedRelItr);
142
143 if (SubRI.r_address != UnsignedRI.r_address)
144 return make_error<JITLinkError>("x86_64 SUBTRACTOR and paired UNSIGNED "
145 "point to different addresses");
146
147 if (SubRI.r_length != UnsignedRI.r_length)
148 return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired "
149 "UNSIGNED reloc must match");
150
151 Symbol *FromSymbol;
152 if (auto FromSymbolOrErr = findSymbolByIndex(SubRI.r_symbolnum))
153 FromSymbol = FromSymbolOrErr->GraphSymbol;
154 else
155 return FromSymbolOrErr.takeError();
156
157 // Read the current fixup value.
158 uint64_t FixupValue = 0;
159 if (SubRI.r_length == 3)
160 FixupValue = *(const little64_t *)FixupContent;
161 else
162 FixupValue = *(const little32_t *)FixupContent;
163
164 // Find 'ToSymbol' using symbol number or address, depending on whether the
165 // paired UNSIGNED relocation is extern.
166 Symbol *ToSymbol = nullptr;
167 if (UnsignedRI.r_extern) {
168 // Find target symbol by symbol index.
169 if (auto ToSymbolOrErr = findSymbolByIndex(UnsignedRI.r_symbolnum))
170 ToSymbol = ToSymbolOrErr->GraphSymbol;
171 else
172 return ToSymbolOrErr.takeError();
173 } else {
174 auto ToSymbolSec = findSectionByIndex(UnsignedRI.r_symbolnum - 1);
175 if (!ToSymbolSec)
176 return ToSymbolSec.takeError();
177 ToSymbol = getSymbolByAddress(*ToSymbolSec, ToSymbolSec->Address);
178 assert(ToSymbol && "No symbol for section");
179 FixupValue -= ToSymbol->getAddress().getValue();
180 }
181
182 Edge::Kind DeltaKind;
183 Symbol *TargetSymbol;
184 uint64_t Addend;
185
186 bool FixingFromSymbol = true;
187 if (&BlockToFix == &FromSymbol->getAddressable()) {
188 if (LLVM_UNLIKELY(&BlockToFix == &ToSymbol->getAddressable())) {
189 // From and To are symbols in the same block. Decide direction by offset
190 // instead.
191 if (ToSymbol->getAddress() > FixupAddress)
192 FixingFromSymbol = true;
193 else if (FromSymbol->getAddress() > FixupAddress)
194 FixingFromSymbol = false;
195 else
196 FixingFromSymbol = FromSymbol->getAddress() >= ToSymbol->getAddress();
197 } else
198 FixingFromSymbol = true;
199 } else {
200 if (&BlockToFix == &ToSymbol->getAddressable())
201 FixingFromSymbol = false;
202 else {
203 // BlockToFix was neither FromSymbol nor ToSymbol.
204 return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "
205 "either 'A' or 'B' (or a symbol in one "
206 "of their alt-entry groups)");
207 }
208 }
209
210 if (FixingFromSymbol) {
211 TargetSymbol = ToSymbol;
212 DeltaKind = (SubRI.r_length == 3) ? x86_64::Delta64 : x86_64::Delta32;
213 Addend = FixupValue + (FixupAddress - FromSymbol->getAddress());
214 // FIXME: handle extern 'from'.
215 } else {
216 TargetSymbol = FromSymbol;
217 DeltaKind =
219 Addend = FixupValue - (FixupAddress - ToSymbol->getAddress());
220 }
221
222 return PairRelocInfo(DeltaKind, TargetSymbol, Addend);
223 }
224
225 Error addRelocations() override {
226 using namespace support;
227 auto &Obj = getObject();
228
229 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
230
231 for (const auto &S : Obj.sections()) {
232
233 orc::ExecutorAddr SectionAddress(S.getAddress());
234
235 // Skip relocations virtual sections.
236 if (S.isVirtual()) {
237 if (S.relocation_begin() != S.relocation_end())
238 return make_error<JITLinkError>("Virtual section contains "
239 "relocations");
240 continue;
241 }
242
243 auto NSec =
244 findSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl()));
245 if (!NSec)
246 return NSec.takeError();
247
248 // Skip relocations for MachO sections without corresponding graph
249 // sections.
250 {
251 if (!NSec->GraphSection) {
252 LLVM_DEBUG({
253 dbgs() << " Skipping relocations for MachO section "
254 << NSec->SegName << "/" << NSec->SectName
255 << " which has no associated graph section\n";
256 });
257 continue;
258 }
259 }
260
261 // Add relocations for section.
262 for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();
263 RelItr != RelEnd; ++RelItr) {
264
266
267 // Find the address of the value to fix up.
268 auto FixupAddress = SectionAddress + (uint32_t)RI.r_address;
269
270 LLVM_DEBUG({
271 dbgs() << " " << NSec->SectName << " + "
272 << formatv("{0:x8}", RI.r_address) << ":\n";
273 });
274
275 // Find the block that the fixup points to.
276 Block *BlockToFix = nullptr;
277 {
278 auto SymbolToFixOrErr = findSymbolByAddress(*NSec, FixupAddress);
279 if (!SymbolToFixOrErr)
280 return SymbolToFixOrErr.takeError();
281 BlockToFix = &SymbolToFixOrErr->getBlock();
282 }
283
284 if (FixupAddress + orc::ExecutorAddrDiff(1ULL << RI.r_length) >
285 BlockToFix->getAddress() + BlockToFix->getContent().size())
286 return make_error<JITLinkError>(
287 "Relocation extends past end of fixup block");
288
289 // Get a pointer to the fixup content.
290 const char *FixupContent = BlockToFix->getContent().data() +
291 (FixupAddress - BlockToFix->getAddress());
292
293 size_t FixupOffset = FixupAddress - BlockToFix->getAddress();
294
295 // The target symbol and addend will be populated by the switch below.
296 Symbol *TargetSymbol = nullptr;
297 uint64_t Addend = 0;
298
299 // Validate the relocation kind.
300 auto MachORelocKind = getRelocKind(RI);
301 if (!MachORelocKind)
302 return MachORelocKind.takeError();
303
305
306 switch (*MachORelocKind) {
307 case MachOBranch32:
308 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
309 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
310 else
311 return TargetSymbolOrErr.takeError();
312 Addend = *(const little32_t *)FixupContent;
314 break;
315 case MachOPCRel32:
316 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
317 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
318 else
319 return TargetSymbolOrErr.takeError();
320 Addend = *(const little32_t *)FixupContent - 4;
321 Kind = x86_64::Delta32;
322 break;
323 case MachOPCRel32GOTLoad:
324 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
325 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
326 else
327 return TargetSymbolOrErr.takeError();
328 Addend = *(const little32_t *)FixupContent;
330 if (FixupOffset < 3)
331 return make_error<JITLinkError>("GOTLD at invalid offset " +
332 formatv("{0}", FixupOffset));
333 break;
334 case MachOPCRel32GOT:
335 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
336 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
337 else
338 return TargetSymbolOrErr.takeError();
339 Addend = *(const little32_t *)FixupContent - 4;
341 break;
342 case MachOPCRel32TLV:
343 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
344 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
345 else
346 return TargetSymbolOrErr.takeError();
347 Addend = *(const little32_t *)FixupContent;
349 if (FixupOffset < 3)
350 return make_error<JITLinkError>("TLV at invalid offset " +
351 formatv("{0}", FixupOffset));
352 break;
353 case MachOPointer32:
354 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
355 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
356 else
357 return TargetSymbolOrErr.takeError();
358 Addend = *(const ulittle32_t *)FixupContent;
359 Kind = x86_64::Pointer32;
360 break;
361 case MachOPointer64:
362 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
363 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
364 else
365 return TargetSymbolOrErr.takeError();
366 Addend = *(const ulittle64_t *)FixupContent;
367 Kind = x86_64::Pointer64;
368 break;
369 case MachOPointer64Anon: {
370 orc::ExecutorAddr TargetAddress(*(const ulittle64_t *)FixupContent);
371 auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);
372 if (!TargetNSec)
373 return TargetNSec.takeError();
374 if (auto TargetSymbolOrErr =
375 findSymbolByAddress(*TargetNSec, TargetAddress))
376 TargetSymbol = &*TargetSymbolOrErr;
377 else
378 return TargetSymbolOrErr.takeError();
379 Addend = TargetAddress - TargetSymbol->getAddress();
380 Kind = x86_64::Pointer64;
381 break;
382 }
383 case MachOPCRel32Minus1:
384 case MachOPCRel32Minus2:
385 case MachOPCRel32Minus4:
386 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
387 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
388 else
389 return TargetSymbolOrErr.takeError();
390 Addend = *(const little32_t *)FixupContent - 4;
391 Kind = x86_64::Delta32;
392 break;
393 case MachOPCRel32Anon: {
394 orc::ExecutorAddr TargetAddress(FixupAddress + 4 +
395 *(const little32_t *)FixupContent);
396 auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);
397 if (!TargetNSec)
398 return TargetNSec.takeError();
399 if (auto TargetSymbolOrErr =
400 findSymbolByAddress(*TargetNSec, TargetAddress))
401 TargetSymbol = &*TargetSymbolOrErr;
402 else
403 return TargetSymbolOrErr.takeError();
404 Addend = TargetAddress - TargetSymbol->getAddress() - 4;
405 Kind = x86_64::Delta32;
406 break;
407 }
408 case MachOPCRel32Minus1Anon:
409 case MachOPCRel32Minus2Anon:
410 case MachOPCRel32Minus4Anon: {
413 1ULL << (*MachORelocKind - MachOPCRel32Minus1Anon));
414 orc::ExecutorAddr TargetAddress =
415 FixupAddress + Delta + *(const little32_t *)FixupContent;
416 auto TargetNSec = findSectionByIndex(RI.r_symbolnum - 1);
417 if (!TargetNSec)
418 return TargetNSec.takeError();
419 if (auto TargetSymbolOrErr =
420 findSymbolByAddress(*TargetNSec, TargetAddress))
421 TargetSymbol = &*TargetSymbolOrErr;
422 else
423 return TargetSymbolOrErr.takeError();
424 Addend = TargetAddress - TargetSymbol->getAddress() - Delta;
425 Kind = x86_64::Delta32;
426 break;
427 }
428 case MachOSubtractor32:
429 case MachOSubtractor64: {
430 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
431 // parsePairRelocation handles the paired reloc, and returns the
432 // edge kind to be used (either Delta32/Delta64, or
433 // NegDelta32/NegDelta64, depending on the direction of the
434 // subtraction) along with the addend.
435 auto PairInfo =
436 parsePairRelocation(*BlockToFix, *MachORelocKind, RI,
437 FixupAddress, FixupContent, ++RelItr, RelEnd);
438 if (!PairInfo)
439 return PairInfo.takeError();
440 std::tie(Kind, TargetSymbol, Addend) = *PairInfo;
441 assert(TargetSymbol && "No target symbol from parsePairRelocation?");
442 break;
443 }
444 }
445
446 LLVM_DEBUG({
447 dbgs() << " ";
448 Edge GE(Kind, FixupAddress - BlockToFix->getAddress(), *TargetSymbol,
449 Addend);
450 printEdge(dbgs(), *BlockToFix, GE, x86_64::getEdgeKindName(Kind));
451 dbgs() << "\n";
452 });
453 BlockToFix->addEdge(Kind, FixupAddress - BlockToFix->getAddress(),
454 *TargetSymbol, Addend);
455 }
456 }
457 return Error::success();
458 }
459};
460
461Error buildGOTAndStubs_MachO_x86_64(LinkGraph &G) {
464 visitExistingEdges(G, GOT, PLT);
465 return Error::success();
466}
467
468} // namespace
469
470namespace llvm {
471namespace jitlink {
472
473class MachOJITLinker_x86_64 : public JITLinker<MachOJITLinker_x86_64> {
474 friend class JITLinker<MachOJITLinker_x86_64>;
475
476public:
477 MachOJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
478 std::unique_ptr<LinkGraph> G,
479 PassConfiguration PassConfig)
480 : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
481
482private:
483 Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
484 return x86_64::applyFixup(G, B, E, nullptr);
485 }
486};
487
489 MemoryBufferRef ObjectBuffer, std::shared_ptr<orc::SymbolStringPool> SSP) {
490 auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjectBuffer);
491 if (!MachOObj)
492 return MachOObj.takeError();
493
494 auto Features = (*MachOObj)->getFeatures();
495 if (!Features)
496 return Features.takeError();
497
498 return MachOLinkGraphBuilder_x86_64(**MachOObj, std::move(SSP),
499 std::move(*Features))
500 .buildGraph();
501}
502
503void link_MachO_x86_64(std::unique_ptr<LinkGraph> G,
504 std::unique_ptr<JITLinkContext> Ctx) {
505
507
508 if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {
509 // Add eh-frame passes.
510 Config.PrePrunePasses.push_back(createEHFrameSplitterPass_MachO_x86_64());
511 Config.PrePrunePasses.push_back(createEHFrameEdgeFixerPass_MachO_x86_64());
512
513 // Add compact unwind splitter pass.
514 Config.PrePrunePasses.push_back(
515 CompactUnwindSplitter("__LD,__compact_unwind"));
516
517 // Add a mark-live pass.
518 if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
519 Config.PrePrunePasses.push_back(std::move(MarkLive));
520 else
521 Config.PrePrunePasses.push_back(markAllSymbolsLive);
522
523 // Resolve any external section start / end symbols.
524 Config.PostAllocationPasses.push_back(
527
528 // Add an in-place GOT/Stubs pass.
529 Config.PostPrunePasses.push_back(buildGOTAndStubs_MachO_x86_64);
530
531 // Add GOT/Stubs optimizer pass.
532 Config.PreFixupPasses.push_back(x86_64::optimizeGOTAndStubAccesses);
533 }
534
535 if (auto Err = Ctx->modifyPassConfig(*G, Config))
536 return Ctx->notifyFailed(std::move(Err));
537
538 // Construct a JITLinker and run the link function.
539 MachOJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));
540}
541
543 return DWARFRecordSectionSplitter("__TEXT,__eh_frame");
544}
545
547 return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize,
550}
551
552} // end namespace jitlink
553} // end namespace llvm
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
Definition: Compiler.h:320
#define LLVM_DEBUG(...)
Definition: Debug.h:106
RelaxConfig Config
Definition: ELF_riscv.cpp:506
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:168
const T * data() const
Definition: ArrayRef.h:165
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
Tagged union holding either a T or a Error.
Definition: Error.h:481
Manages the enabling and disabling of subtarget specific features.
Triple - Helper class for working with autoconf configuration names.
Definition: Triple.h:44
uint64_t getSectionIndex(DataRefImpl Sec) const override
static Expected< std::unique_ptr< MachOObjectFile > > createMachOObjectFile(MemoryBufferRef Object, uint32_t UniversalCputype=0, uint32_t UniversalIndex=0, size_t MachOFilesetEntryOffset=0)
Create a MachOObjectFile instance from a given buffer.
section_iterator_range sections() const
Definition: ObjectFile.h:329
Represents an address in the executor process.
uint64_t getValue() const
unique_function is a type-erasing functor similar to std::function.
@ X86_64_RELOC_UNSIGNED
Definition: MachO.h:483
@ X86_64_RELOC_SIGNED
Definition: MachO.h:484
@ X86_64_RELOC_GOT
Definition: MachO.h:487
@ X86_64_RELOC_GOT_LOAD
Definition: MachO.h:486
@ X86_64_RELOC_BRANCH
Definition: MachO.h:485
@ X86_64_RELOC_SIGNED_2
Definition: MachO.h:490
@ X86_64_RELOC_TLV
Definition: MachO.h:492
@ X86_64_RELOC_SIGNED_4
Definition: MachO.h:491
@ X86_64_RELOC_SUBTRACTOR
Definition: MachO.h:488
@ X86_64_RELOC_SIGNED_1
Definition: MachO.h:489
uint64_t ExecutorAddrDiff
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition: STLExtras.h:1873
Implement std::hash so that hash_code can be used in STL containers.
Definition: BitVector.h:858