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