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
13 #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"
14
15 #include "BasicGOTAndStubsBuilder.h"
16 #include "MachOLinkGraphBuilder.h"
17
18 #define DEBUG_TYPE "jitlink"
19
20 using namespace llvm;
21 using namespace llvm::jitlink;
22 using namespace llvm::jitlink::MachO_x86_64_Edges;
23
24 namespace {
25
26 class MachOLinkGraphBuilder_x86_64 : public MachOLinkGraphBuilder {
27 public:
MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile & Obj)28 MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile &Obj)
29 : MachOLinkGraphBuilder(Obj, Triple("x86_64-apple-darwin")) {}
30
31 private:
32 static Expected<MachOX86RelocationKind>
getRelocationKind(const MachO::relocation_info & RI)33 getRelocationKind(const MachO::relocation_info &RI) {
34 switch (RI.r_type) {
35 case MachO::X86_64_RELOC_UNSIGNED:
36 if (!RI.r_pcrel) {
37 if (RI.r_length == 3)
38 return RI.r_extern ? Pointer64 : Pointer64Anon;
39 else if (RI.r_extern && RI.r_length == 2)
40 return Pointer32;
41 }
42 break;
43 case MachO::X86_64_RELOC_SIGNED:
44 if (RI.r_pcrel && RI.r_length == 2)
45 return RI.r_extern ? PCRel32 : PCRel32Anon;
46 break;
47 case MachO::X86_64_RELOC_BRANCH:
48 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
49 return Branch32;
50 break;
51 case MachO::X86_64_RELOC_GOT_LOAD:
52 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
53 return PCRel32GOTLoad;
54 break;
55 case MachO::X86_64_RELOC_GOT:
56 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
57 return PCRel32GOT;
58 break;
59 case MachO::X86_64_RELOC_SUBTRACTOR:
60 // SUBTRACTOR must be non-pc-rel, extern, with length 2 or 3.
61 // Initially represent SUBTRACTOR relocations with 'Delta<W>'. They may
62 // be turned into NegDelta<W> by parsePairRelocation.
63 if (!RI.r_pcrel && RI.r_extern) {
64 if (RI.r_length == 2)
65 return Delta32;
66 else if (RI.r_length == 3)
67 return Delta64;
68 }
69 break;
70 case MachO::X86_64_RELOC_SIGNED_1:
71 if (RI.r_pcrel && RI.r_length == 2)
72 return RI.r_extern ? PCRel32Minus1 : PCRel32Minus1Anon;
73 break;
74 case MachO::X86_64_RELOC_SIGNED_2:
75 if (RI.r_pcrel && RI.r_length == 2)
76 return RI.r_extern ? PCRel32Minus2 : PCRel32Minus2Anon;
77 break;
78 case MachO::X86_64_RELOC_SIGNED_4:
79 if (RI.r_pcrel && RI.r_length == 2)
80 return RI.r_extern ? PCRel32Minus4 : PCRel32Minus4Anon;
81 break;
82 case MachO::X86_64_RELOC_TLV:
83 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
84 return PCRel32TLV;
85 break;
86 }
87
88 return make_error<JITLinkError>(
89 "Unsupported x86-64 relocation: address=" +
90 formatv("{0:x8}", RI.r_address) +
91 ", symbolnum=" + formatv("{0:x6}", RI.r_symbolnum) +
92 ", kind=" + formatv("{0:x1}", RI.r_type) +
93 ", pc_rel=" + (RI.r_pcrel ? "true" : "false") +
94 ", extern=" + (RI.r_extern ? "true" : "false") +
95 ", length=" + formatv("{0:d}", RI.r_length));
96 }
97
98 using PairRelocInfo = std::tuple<MachOX86RelocationKind, Symbol *, uint64_t>;
99
100 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
101 // returns the edge kind and addend to be used.
102 Expected<PairRelocInfo>
parsePairRelocation(Block & BlockToFix,Edge::Kind SubtractorKind,const MachO::relocation_info & SubRI,JITTargetAddress FixupAddress,const char * FixupContent,object::relocation_iterator & UnsignedRelItr,object::relocation_iterator & RelEnd)103 parsePairRelocation(Block &BlockToFix, Edge::Kind SubtractorKind,
104 const MachO::relocation_info &SubRI,
105 JITTargetAddress FixupAddress, const char *FixupContent,
106 object::relocation_iterator &UnsignedRelItr,
107 object::relocation_iterator &RelEnd) {
108 using namespace support;
109
110 assert(((SubtractorKind == Delta32 && SubRI.r_length == 2) ||
111 (SubtractorKind == Delta64 && SubRI.r_length == 3)) &&
112 "Subtractor kind should match length");
113 assert(SubRI.r_extern && "SUBTRACTOR reloc symbol should be extern");
114 assert(!SubRI.r_pcrel && "SUBTRACTOR reloc should not be PCRel");
115
116 if (UnsignedRelItr == RelEnd)
117 return make_error<JITLinkError>("x86_64 SUBTRACTOR without paired "
118 "UNSIGNED relocation");
119
120 auto UnsignedRI = getRelocationInfo(UnsignedRelItr);
121
122 if (SubRI.r_address != UnsignedRI.r_address)
123 return make_error<JITLinkError>("x86_64 SUBTRACTOR and paired UNSIGNED "
124 "point to different addresses");
125
126 if (SubRI.r_length != UnsignedRI.r_length)
127 return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired "
128 "UNSIGNED reloc must match");
129
130 Symbol *FromSymbol;
131 if (auto FromSymbolOrErr = findSymbolByIndex(SubRI.r_symbolnum))
132 FromSymbol = FromSymbolOrErr->GraphSymbol;
133 else
134 return FromSymbolOrErr.takeError();
135
136 // Read the current fixup value.
137 uint64_t FixupValue = 0;
138 if (SubRI.r_length == 3)
139 FixupValue = *(const little64_t *)FixupContent;
140 else
141 FixupValue = *(const little32_t *)FixupContent;
142
143 // Find 'ToSymbol' using symbol number or address, depending on whether the
144 // paired UNSIGNED relocation is extern.
145 Symbol *ToSymbol = nullptr;
146 if (UnsignedRI.r_extern) {
147 // Find target symbol by symbol index.
148 if (auto ToSymbolOrErr = findSymbolByIndex(UnsignedRI.r_symbolnum))
149 ToSymbol = ToSymbolOrErr->GraphSymbol;
150 else
151 return ToSymbolOrErr.takeError();
152 } else {
153 auto ToSymbolSec = findSectionByIndex(UnsignedRI.r_symbolnum - 1);
154 if (!ToSymbolSec)
155 return ToSymbolSec.takeError();
156 ToSymbol = getSymbolByAddress(ToSymbolSec->Address);
157 assert(ToSymbol && "No symbol for section");
158 FixupValue -= ToSymbol->getAddress();
159 }
160
161 MachOX86RelocationKind DeltaKind;
162 Symbol *TargetSymbol;
163 uint64_t Addend;
164 if (&BlockToFix == &FromSymbol->getAddressable()) {
165 TargetSymbol = ToSymbol;
166 DeltaKind = (SubRI.r_length == 3) ? Delta64 : Delta32;
167 Addend = FixupValue + (FixupAddress - FromSymbol->getAddress());
168 // FIXME: handle extern 'from'.
169 } else if (&BlockToFix == &ToSymbol->getAddressable()) {
170 TargetSymbol = FromSymbol;
171 DeltaKind = (SubRI.r_length == 3) ? NegDelta64 : NegDelta32;
172 Addend = FixupValue - (FixupAddress - ToSymbol->getAddress());
173 } else {
174 // BlockToFix was neither FromSymbol nor ToSymbol.
175 return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "
176 "either 'A' or 'B' (or a symbol in one "
177 "of their alt-entry chains)");
178 }
179
180 return PairRelocInfo(DeltaKind, TargetSymbol, Addend);
181 }
182
addRelocations()183 Error addRelocations() override {
184 using namespace support;
185 auto &Obj = getObject();
186
187 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
188
189 for (auto &S : Obj.sections()) {
190
191 JITTargetAddress SectionAddress = S.getAddress();
192
193 // Skip relocations virtual sections.
194 if (S.isVirtual()) {
195 if (S.relocation_begin() != S.relocation_end())
196 return make_error<JITLinkError>("Virtual section contains "
197 "relocations");
198 continue;
199 }
200
201 // Skip relocations for debug symbols.
202 {
203 auto &NSec =
204 getSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl()));
205 if (!NSec.GraphSection) {
206 LLVM_DEBUG({
207 dbgs() << " Skipping relocations for MachO section "
208 << NSec.SegName << "/" << NSec.SectName
209 << " which has no associated graph section\n";
210 });
211 continue;
212 }
213 }
214
215 // Add relocations for section.
216 for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();
217 RelItr != RelEnd; ++RelItr) {
218
219 MachO::relocation_info RI = getRelocationInfo(RelItr);
220
221 // Sanity check the relocation kind.
222 auto Kind = getRelocationKind(RI);
223 if (!Kind)
224 return Kind.takeError();
225
226 // Find the address of the value to fix up.
227 JITTargetAddress FixupAddress = SectionAddress + (uint32_t)RI.r_address;
228
229 LLVM_DEBUG({
230 auto &NSec =
231 getSectionByIndex(Obj.getSectionIndex(S.getRawDataRefImpl()));
232 dbgs() << " " << NSec.SectName << " + "
233 << formatv("{0:x8}", RI.r_address) << ":\n";
234 });
235
236 // Find the block that the fixup points to.
237 Block *BlockToFix = nullptr;
238 {
239 auto SymbolToFixOrErr = findSymbolByAddress(FixupAddress);
240 if (!SymbolToFixOrErr)
241 return SymbolToFixOrErr.takeError();
242 BlockToFix = &SymbolToFixOrErr->getBlock();
243 }
244
245 if (FixupAddress + static_cast<JITTargetAddress>(1ULL << RI.r_length) >
246 BlockToFix->getAddress() + BlockToFix->getContent().size())
247 return make_error<JITLinkError>(
248 "Relocation extends past end of fixup block");
249
250 // Get a pointer to the fixup content.
251 const char *FixupContent = BlockToFix->getContent().data() +
252 (FixupAddress - BlockToFix->getAddress());
253
254 // The target symbol and addend will be populated by the switch below.
255 Symbol *TargetSymbol = nullptr;
256 uint64_t Addend = 0;
257
258 switch (*Kind) {
259 case Branch32:
260 case PCRel32:
261 case PCRel32GOTLoad:
262 case PCRel32GOT:
263 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
264 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
265 else
266 return TargetSymbolOrErr.takeError();
267 Addend = *(const little32_t *)FixupContent;
268 break;
269 case Pointer32:
270 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
271 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
272 else
273 return TargetSymbolOrErr.takeError();
274 Addend = *(const ulittle32_t *)FixupContent;
275 break;
276 case Pointer64:
277 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
278 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
279 else
280 return TargetSymbolOrErr.takeError();
281 Addend = *(const ulittle64_t *)FixupContent;
282 break;
283 case Pointer64Anon: {
284 JITTargetAddress TargetAddress = *(const ulittle64_t *)FixupContent;
285 if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
286 TargetSymbol = &*TargetSymbolOrErr;
287 else
288 return TargetSymbolOrErr.takeError();
289 Addend = TargetAddress - TargetSymbol->getAddress();
290 break;
291 }
292 case PCRel32Minus1:
293 case PCRel32Minus2:
294 case PCRel32Minus4:
295 if (auto TargetSymbolOrErr = findSymbolByIndex(RI.r_symbolnum))
296 TargetSymbol = TargetSymbolOrErr->GraphSymbol;
297 else
298 return TargetSymbolOrErr.takeError();
299 Addend = *(const little32_t *)FixupContent +
300 (1 << (*Kind - PCRel32Minus1));
301 break;
302 case PCRel32Anon: {
303 JITTargetAddress TargetAddress =
304 FixupAddress + 4 + *(const little32_t *)FixupContent;
305 if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
306 TargetSymbol = &*TargetSymbolOrErr;
307 else
308 return TargetSymbolOrErr.takeError();
309 Addend = TargetAddress - TargetSymbol->getAddress();
310 break;
311 }
312 case PCRel32Minus1Anon:
313 case PCRel32Minus2Anon:
314 case PCRel32Minus4Anon: {
315 JITTargetAddress Delta =
316 static_cast<JITTargetAddress>(1ULL << (*Kind - PCRel32Minus1Anon));
317 JITTargetAddress TargetAddress =
318 FixupAddress + 4 + Delta + *(const little32_t *)FixupContent;
319 if (auto TargetSymbolOrErr = findSymbolByAddress(TargetAddress))
320 TargetSymbol = &*TargetSymbolOrErr;
321 else
322 return TargetSymbolOrErr.takeError();
323 Addend = TargetAddress - TargetSymbol->getAddress();
324 break;
325 }
326 case Delta32:
327 case Delta64: {
328 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
329 // parsePairRelocation handles the paired reloc, and returns the
330 // edge kind to be used (either Delta32/Delta64, or
331 // NegDelta32/NegDelta64, depending on the direction of the
332 // subtraction) along with the addend.
333 auto PairInfo =
334 parsePairRelocation(*BlockToFix, *Kind, RI, FixupAddress,
335 FixupContent, ++RelItr, RelEnd);
336 if (!PairInfo)
337 return PairInfo.takeError();
338 std::tie(*Kind, TargetSymbol, Addend) = *PairInfo;
339 assert(TargetSymbol && "No target symbol from parsePairRelocation?");
340 break;
341 }
342 case PCRel32TLV:
343 return make_error<JITLinkError>(
344 "MachO TLV relocations not yet supported");
345 default:
346 llvm_unreachable("Special relocation kind should not appear in "
347 "mach-o file");
348 }
349
350 LLVM_DEBUG({
351 dbgs() << " ";
352 Edge GE(*Kind, FixupAddress - BlockToFix->getAddress(), *TargetSymbol,
353 Addend);
354 printEdge(dbgs(), *BlockToFix, GE,
355 getMachOX86RelocationKindName(*Kind));
356 dbgs() << "\n";
357 });
358 BlockToFix->addEdge(*Kind, FixupAddress - BlockToFix->getAddress(),
359 *TargetSymbol, Addend);
360 }
361 }
362 return Error::success();
363 }
364 };
365
366 class MachO_x86_64_GOTAndStubsBuilder
367 : public BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder> {
368 public:
369 static const uint8_t NullGOTEntryContent[8];
370 static const uint8_t StubContent[6];
371
MachO_x86_64_GOTAndStubsBuilder(LinkGraph & G)372 MachO_x86_64_GOTAndStubsBuilder(LinkGraph &G)
373 : BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder>(G) {}
374
isGOTEdge(Edge & E) const375 bool isGOTEdge(Edge &E) const {
376 return E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad;
377 }
378
createGOTEntry(Symbol & Target)379 Symbol &createGOTEntry(Symbol &Target) {
380 auto &GOTEntryBlock = G.createContentBlock(
381 getGOTSection(), getGOTEntryBlockContent(), 0, 8, 0);
382 GOTEntryBlock.addEdge(Pointer64, 0, Target, 0);
383 return G.addAnonymousSymbol(GOTEntryBlock, 0, 8, false, false);
384 }
385
fixGOTEdge(Edge & E,Symbol & GOTEntry)386 void fixGOTEdge(Edge &E, Symbol &GOTEntry) {
387 assert((E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad) &&
388 "Not a GOT edge?");
389 // If this is a PCRel32GOT then change it to an ordinary PCRel32. If it is
390 // a PCRel32GOTLoad then leave it as-is for now. We will use the kind to
391 // check for GOT optimization opportunities in the
392 // optimizeMachO_x86_64_GOTAndStubs pass below.
393 if (E.getKind() == PCRel32GOT)
394 E.setKind(PCRel32);
395
396 E.setTarget(GOTEntry);
397 // Leave the edge addend as-is.
398 }
399
isExternalBranchEdge(Edge & E)400 bool isExternalBranchEdge(Edge &E) {
401 return E.getKind() == Branch32 && !E.getTarget().isDefined();
402 }
403
createStub(Symbol & Target)404 Symbol &createStub(Symbol &Target) {
405 auto &StubContentBlock =
406 G.createContentBlock(getStubsSection(), getStubBlockContent(), 0, 1, 0);
407 // Re-use GOT entries for stub targets.
408 auto &GOTEntrySymbol = getGOTEntrySymbol(Target);
409 StubContentBlock.addEdge(PCRel32, 2, GOTEntrySymbol, 0);
410 return G.addAnonymousSymbol(StubContentBlock, 0, 6, true, false);
411 }
412
fixExternalBranchEdge(Edge & E,Symbol & Stub)413 void fixExternalBranchEdge(Edge &E, Symbol &Stub) {
414 assert(E.getKind() == Branch32 && "Not a Branch32 edge?");
415 assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?");
416
417 // Set the edge kind to Branch32ToStub. We will use this to check for stub
418 // optimization opportunities in the optimizeMachO_x86_64_GOTAndStubs pass
419 // below.
420 E.setKind(Branch32ToStub);
421 E.setTarget(Stub);
422 }
423
424 private:
getGOTSection()425 Section &getGOTSection() {
426 if (!GOTSection)
427 GOTSection = &G.createSection("$__GOT", sys::Memory::MF_READ);
428 return *GOTSection;
429 }
430
getStubsSection()431 Section &getStubsSection() {
432 if (!StubsSection) {
433 auto StubsProt = static_cast<sys::Memory::ProtectionFlags>(
434 sys::Memory::MF_READ | sys::Memory::MF_EXEC);
435 StubsSection = &G.createSection("$__STUBS", StubsProt);
436 }
437 return *StubsSection;
438 }
439
getGOTEntryBlockContent()440 StringRef getGOTEntryBlockContent() {
441 return StringRef(reinterpret_cast<const char *>(NullGOTEntryContent),
442 sizeof(NullGOTEntryContent));
443 }
444
getStubBlockContent()445 StringRef getStubBlockContent() {
446 return StringRef(reinterpret_cast<const char *>(StubContent),
447 sizeof(StubContent));
448 }
449
450 Section *GOTSection = nullptr;
451 Section *StubsSection = nullptr;
452 };
453
454 const uint8_t MachO_x86_64_GOTAndStubsBuilder::NullGOTEntryContent[8] = {
455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
456 const uint8_t MachO_x86_64_GOTAndStubsBuilder::StubContent[6] = {
457 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
458 } // namespace
459
optimizeMachO_x86_64_GOTAndStubs(LinkGraph & G)460 static Error optimizeMachO_x86_64_GOTAndStubs(LinkGraph &G) {
461 LLVM_DEBUG(dbgs() << "Optimizing GOT entries and stubs:\n");
462
463 for (auto *B : G.blocks())
464 for (auto &E : B->edges())
465 if (E.getKind() == PCRel32GOTLoad) {
466 assert(E.getOffset() >= 3 && "GOT edge occurs too early in block");
467
468 // Switch the edge kind to PCRel32: Whether we change the edge target
469 // or not this will be the desired kind.
470 E.setKind(PCRel32);
471
472 // Optimize GOT references.
473 auto &GOTBlock = E.getTarget().getBlock();
474 assert(GOTBlock.getSize() == G.getPointerSize() &&
475 "GOT entry block should be pointer sized");
476 assert(GOTBlock.edges_size() == 1 &&
477 "GOT entry should only have one outgoing edge");
478
479 auto &GOTTarget = GOTBlock.edges().begin()->getTarget();
480 JITTargetAddress EdgeAddr = B->getAddress() + E.getOffset();
481 JITTargetAddress TargetAddr = GOTTarget.getAddress();
482
483 // Check that this is a recognized MOV instruction.
484 // FIXME: Can we assume this?
485 constexpr uint8_t MOVQRIPRel[] = {0x48, 0x8b};
486 if (strncmp(B->getContent().data() + E.getOffset() - 3,
487 reinterpret_cast<const char *>(MOVQRIPRel), 2) != 0)
488 continue;
489
490 int64_t Displacement = TargetAddr - EdgeAddr + 4;
491 if (Displacement >= std::numeric_limits<int32_t>::min() &&
492 Displacement <= std::numeric_limits<int32_t>::max()) {
493 E.setTarget(GOTTarget);
494 auto *BlockData = reinterpret_cast<uint8_t *>(
495 const_cast<char *>(B->getContent().data()));
496 BlockData[E.getOffset() - 2] = 0x8d;
497 LLVM_DEBUG({
498 dbgs() << " Replaced GOT load wih LEA:\n ";
499 printEdge(dbgs(), *B, E,
500 getMachOX86RelocationKindName(E.getKind()));
501 dbgs() << "\n";
502 });
503 }
504 } else if (E.getKind() == Branch32ToStub) {
505
506 // Switch the edge kind to PCRel32: Whether we change the edge target
507 // or not this will be the desired kind.
508 E.setKind(Branch32);
509
510 auto &StubBlock = E.getTarget().getBlock();
511 assert(StubBlock.getSize() ==
512 sizeof(MachO_x86_64_GOTAndStubsBuilder::StubContent) &&
513 "Stub block should be stub sized");
514 assert(StubBlock.edges_size() == 1 &&
515 "Stub block should only have one outgoing edge");
516
517 auto &GOTBlock = StubBlock.edges().begin()->getTarget().getBlock();
518 assert(GOTBlock.getSize() == G.getPointerSize() &&
519 "GOT block should be pointer sized");
520 assert(GOTBlock.edges_size() == 1 &&
521 "GOT block should only have one outgoing edge");
522
523 auto &GOTTarget = GOTBlock.edges().begin()->getTarget();
524 JITTargetAddress EdgeAddr = B->getAddress() + E.getOffset();
525 JITTargetAddress TargetAddr = GOTTarget.getAddress();
526
527 int64_t Displacement = TargetAddr - EdgeAddr + 4;
528 if (Displacement >= std::numeric_limits<int32_t>::min() &&
529 Displacement <= std::numeric_limits<int32_t>::max()) {
530 E.setTarget(GOTTarget);
531 LLVM_DEBUG({
532 dbgs() << " Replaced stub branch with direct branch:\n ";
533 printEdge(dbgs(), *B, E,
534 getMachOX86RelocationKindName(E.getKind()));
535 dbgs() << "\n";
536 });
537 }
538 }
539
540 return Error::success();
541 }
542
543 namespace llvm {
544 namespace jitlink {
545
546 class MachOJITLinker_x86_64 : public JITLinker<MachOJITLinker_x86_64> {
547 friend class JITLinker<MachOJITLinker_x86_64>;
548
549 public:
MachOJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,std::unique_ptr<LinkGraph> G,PassConfiguration PassConfig)550 MachOJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
551 std::unique_ptr<LinkGraph> G,
552 PassConfiguration PassConfig)
553 : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
554
555 private:
getEdgeKindName(Edge::Kind R) const556 StringRef getEdgeKindName(Edge::Kind R) const override {
557 return getMachOX86RelocationKindName(R);
558 }
559
targetOutOfRangeError(const Block & B,const Edge & E)560 static Error targetOutOfRangeError(const Block &B, const Edge &E) {
561 std::string ErrMsg;
562 {
563 raw_string_ostream ErrStream(ErrMsg);
564 ErrStream << "Relocation target out of range: ";
565 printEdge(ErrStream, B, E, getMachOX86RelocationKindName(E.getKind()));
566 ErrStream << "\n";
567 }
568 return make_error<JITLinkError>(std::move(ErrMsg));
569 }
570
applyFixup(Block & B,const Edge & E,char * BlockWorkingMem) const571 Error applyFixup(Block &B, const Edge &E, char *BlockWorkingMem) const {
572
573 using namespace support;
574
575 char *FixupPtr = BlockWorkingMem + E.getOffset();
576 JITTargetAddress FixupAddress = B.getAddress() + E.getOffset();
577
578 switch (E.getKind()) {
579 case Branch32:
580 case PCRel32:
581 case PCRel32Anon: {
582 int64_t Value =
583 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
584 if (Value < std::numeric_limits<int32_t>::min() ||
585 Value > std::numeric_limits<int32_t>::max())
586 return targetOutOfRangeError(B, E);
587 *(little32_t *)FixupPtr = Value;
588 break;
589 }
590 case Pointer64:
591 case Pointer64Anon: {
592 uint64_t Value = E.getTarget().getAddress() + E.getAddend();
593 *(ulittle64_t *)FixupPtr = Value;
594 break;
595 }
596 case PCRel32Minus1:
597 case PCRel32Minus2:
598 case PCRel32Minus4: {
599 int Delta = 4 + (1 << (E.getKind() - PCRel32Minus1));
600 int64_t Value =
601 E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
602 if (Value < std::numeric_limits<int32_t>::min() ||
603 Value > std::numeric_limits<int32_t>::max())
604 return targetOutOfRangeError(B, E);
605 *(little32_t *)FixupPtr = Value;
606 break;
607 }
608 case PCRel32Minus1Anon:
609 case PCRel32Minus2Anon:
610 case PCRel32Minus4Anon: {
611 int Delta = 4 + (1 << (E.getKind() - PCRel32Minus1Anon));
612 int64_t Value =
613 E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
614 if (Value < std::numeric_limits<int32_t>::min() ||
615 Value > std::numeric_limits<int32_t>::max())
616 return targetOutOfRangeError(B, E);
617 *(little32_t *)FixupPtr = Value;
618 break;
619 }
620 case Delta32:
621 case Delta64:
622 case NegDelta32:
623 case NegDelta64: {
624 int64_t Value;
625 if (E.getKind() == Delta32 || E.getKind() == Delta64)
626 Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
627 else
628 Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
629
630 if (E.getKind() == Delta32 || E.getKind() == NegDelta32) {
631 if (Value < std::numeric_limits<int32_t>::min() ||
632 Value > std::numeric_limits<int32_t>::max())
633 return targetOutOfRangeError(B, E);
634 *(little32_t *)FixupPtr = Value;
635 } else
636 *(little64_t *)FixupPtr = Value;
637 break;
638 }
639 case Pointer32: {
640 uint64_t Value = E.getTarget().getAddress() + E.getAddend();
641 if (Value > std::numeric_limits<uint32_t>::max())
642 return targetOutOfRangeError(B, E);
643 *(ulittle32_t *)FixupPtr = Value;
644 break;
645 }
646 default:
647 llvm_unreachable("Unrecognized edge kind");
648 }
649
650 return Error::success();
651 }
652
653 uint64_t NullValue = 0;
654 };
655
656 Expected<std::unique_ptr<LinkGraph>>
createLinkGraphFromMachOObject_x86_64(MemoryBufferRef ObjectBuffer)657 createLinkGraphFromMachOObject_x86_64(MemoryBufferRef ObjectBuffer) {
658 auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjectBuffer);
659 if (!MachOObj)
660 return MachOObj.takeError();
661 return MachOLinkGraphBuilder_x86_64(**MachOObj).buildGraph();
662 }
663
link_MachO_x86_64(std::unique_ptr<LinkGraph> G,std::unique_ptr<JITLinkContext> Ctx)664 void link_MachO_x86_64(std::unique_ptr<LinkGraph> G,
665 std::unique_ptr<JITLinkContext> Ctx) {
666
667 PassConfiguration Config;
668
669 if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) {
670 // Add eh-frame passses.
671 Config.PrePrunePasses.push_back(EHFrameSplitter("__eh_frame"));
672 Config.PrePrunePasses.push_back(EHFrameEdgeFixer(
673 "__eh_frame", G->getPointerSize(), Delta64, Delta32, NegDelta32));
674
675 // Add a mark-live pass.
676 if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple()))
677 Config.PrePrunePasses.push_back(std::move(MarkLive));
678 else
679 Config.PrePrunePasses.push_back(markAllSymbolsLive);
680
681 // Add an in-place GOT/Stubs pass.
682 Config.PostPrunePasses.push_back([](LinkGraph &G) -> Error {
683 MachO_x86_64_GOTAndStubsBuilder(G).run();
684 return Error::success();
685 });
686
687 // Add GOT/Stubs optimizer pass.
688 Config.PreFixupPasses.push_back(optimizeMachO_x86_64_GOTAndStubs);
689 }
690
691 if (auto Err = Ctx->modifyPassConfig(G->getTargetTriple(), Config))
692 return Ctx->notifyFailed(std::move(Err));
693
694 // Construct a JITLinker and run the link function.
695 MachOJITLinker_x86_64::link(std::move(Ctx), std::move(G), std::move(Config));
696 }
697
getMachOX86RelocationKindName(Edge::Kind R)698 StringRef getMachOX86RelocationKindName(Edge::Kind R) {
699 switch (R) {
700 case Branch32:
701 return "Branch32";
702 case Branch32ToStub:
703 return "Branch32ToStub";
704 case Pointer32:
705 return "Pointer32";
706 case Pointer64:
707 return "Pointer64";
708 case Pointer64Anon:
709 return "Pointer64Anon";
710 case PCRel32:
711 return "PCRel32";
712 case PCRel32Minus1:
713 return "PCRel32Minus1";
714 case PCRel32Minus2:
715 return "PCRel32Minus2";
716 case PCRel32Minus4:
717 return "PCRel32Minus4";
718 case PCRel32Anon:
719 return "PCRel32Anon";
720 case PCRel32Minus1Anon:
721 return "PCRel32Minus1Anon";
722 case PCRel32Minus2Anon:
723 return "PCRel32Minus2Anon";
724 case PCRel32Minus4Anon:
725 return "PCRel32Minus4Anon";
726 case PCRel32GOTLoad:
727 return "PCRel32GOTLoad";
728 case PCRel32GOT:
729 return "PCRel32GOT";
730 case PCRel32TLV:
731 return "PCRel32TLV";
732 case Delta32:
733 return "Delta32";
734 case Delta64:
735 return "Delta64";
736 case NegDelta32:
737 return "NegDelta32";
738 case NegDelta64:
739 return "NegDelta64";
740 default:
741 return getGenericEdgeKindName(static_cast<Edge::Kind>(R));
742 }
743 }
744
745 } // end namespace jitlink
746 } // end namespace llvm
747