1 //===---- MachO_x86_64.cpp -JIT linker implementation for MachO/x86-64 ----===//
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
7 //===----------------------------------------------------------------------===//
9 // MachO/x86-64 jit-link implementation.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/ExecutionEngine/JITLink/MachO_x86_64.h"
15 #include "BasicGOTAndStubsBuilder.h"
16 #include "MachOLinkGraphBuilder.h"
18 #define DEBUG_TYPE "jitlink"
21 using namespace llvm::jitlink
;
22 using namespace llvm::jitlink::MachO_x86_64_Edges
;
26 class MachOLinkGraphBuilder_x86_64
: public MachOLinkGraphBuilder
{
28 MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile
&Obj
)
29 : MachOLinkGraphBuilder(Obj
) {
30 addCustomSectionParser(
31 "__eh_frame", [this](NormalizedSection
&EHFrameSection
) {
32 if (!EHFrameSection
.Data
)
33 return make_error
<JITLinkError
>(
34 "__eh_frame section is marked zero-fill");
35 return MachOEHFrameBinaryParser(
36 *this, EHFrameSection
.Address
,
37 StringRef(EHFrameSection
.Data
, EHFrameSection
.Size
),
38 *EHFrameSection
.GraphSection
, 8, 4, NegDelta32
, Delta64
)
44 static Expected
<MachOX86RelocationKind
>
45 getRelocationKind(const MachO::relocation_info
&RI
) {
47 case MachO::X86_64_RELOC_UNSIGNED
:
50 return RI
.r_extern
? Pointer64
: Pointer64Anon
;
51 else if (RI
.r_extern
&& RI
.r_length
== 2)
55 case MachO::X86_64_RELOC_SIGNED
:
56 if (RI
.r_pcrel
&& RI
.r_length
== 2)
57 return RI
.r_extern
? PCRel32
: PCRel32Anon
;
59 case MachO::X86_64_RELOC_BRANCH
:
60 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
63 case MachO::X86_64_RELOC_GOT_LOAD
:
64 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
65 return PCRel32GOTLoad
;
67 case MachO::X86_64_RELOC_GOT
:
68 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
71 case MachO::X86_64_RELOC_SUBTRACTOR
:
72 // SUBTRACTOR must be non-pc-rel, extern, with length 2 or 3.
73 // Initially represent SUBTRACTOR relocations with 'Delta<W>'. They may
74 // be turned into NegDelta<W> by parsePairRelocation.
75 if (!RI
.r_pcrel
&& RI
.r_extern
) {
78 else if (RI
.r_length
== 3)
82 case MachO::X86_64_RELOC_SIGNED_1
:
83 if (RI
.r_pcrel
&& RI
.r_length
== 2)
84 return RI
.r_extern
? PCRel32Minus1
: PCRel32Minus1Anon
;
86 case MachO::X86_64_RELOC_SIGNED_2
:
87 if (RI
.r_pcrel
&& RI
.r_length
== 2)
88 return RI
.r_extern
? PCRel32Minus2
: PCRel32Minus2Anon
;
90 case MachO::X86_64_RELOC_SIGNED_4
:
91 if (RI
.r_pcrel
&& RI
.r_length
== 2)
92 return RI
.r_extern
? PCRel32Minus4
: PCRel32Minus4Anon
;
94 case MachO::X86_64_RELOC_TLV
:
95 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
100 return make_error
<JITLinkError
>(
101 "Unsupported x86-64 relocation: address=" +
102 formatv("{0:x8}", RI
.r_address
) +
103 ", symbolnum=" + formatv("{0:x6}", RI
.r_symbolnum
) +
104 ", kind=" + formatv("{0:x1}", RI
.r_type
) +
105 ", pc_rel=" + (RI
.r_pcrel
? "true" : "false") +
106 ", extern=" + (RI
.r_extern
? "true" : "false") +
107 ", length=" + formatv("{0:d}", RI
.r_length
));
110 MachO::relocation_info
111 getRelocationInfo(const object::relocation_iterator RelItr
) {
112 MachO::any_relocation_info ARI
=
113 getObject().getRelocation(RelItr
->getRawDataRefImpl());
114 MachO::relocation_info RI
;
115 memcpy(&RI
, &ARI
, sizeof(MachO::relocation_info
));
119 using PairRelocInfo
= std::tuple
<MachOX86RelocationKind
, Symbol
*, uint64_t>;
121 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
122 // returns the edge kind and addend to be used.
123 Expected
<PairRelocInfo
>
124 parsePairRelocation(Block
&BlockToFix
, Edge::Kind SubtractorKind
,
125 const MachO::relocation_info
&SubRI
,
126 JITTargetAddress FixupAddress
, const char *FixupContent
,
127 object::relocation_iterator
&UnsignedRelItr
,
128 object::relocation_iterator
&RelEnd
) {
129 using namespace support
;
131 assert(((SubtractorKind
== Delta32
&& SubRI
.r_length
== 2) ||
132 (SubtractorKind
== Delta64
&& 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");
137 if (UnsignedRelItr
== RelEnd
)
138 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR without paired "
139 "UNSIGNED relocation");
141 auto UnsignedRI
= getRelocationInfo(UnsignedRelItr
);
143 if (SubRI
.r_address
!= UnsignedRI
.r_address
)
144 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR and paired UNSIGNED "
145 "point to different addresses");
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");
152 if (auto FromSymbolOrErr
= findSymbolByIndex(SubRI
.r_symbolnum
))
153 FromSymbol
= FromSymbolOrErr
->GraphSymbol
;
155 return FromSymbolOrErr
.takeError();
157 // Read the current fixup value.
158 uint64_t FixupValue
= 0;
159 if (SubRI
.r_length
== 3)
160 FixupValue
= *(const little64_t
*)FixupContent
;
162 FixupValue
= *(const little32_t
*)FixupContent
;
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
;
172 return ToSymbolOrErr
.takeError();
174 if (auto ToSymbolOrErr
= findSymbolByAddress(FixupValue
))
175 ToSymbol
= &*ToSymbolOrErr
;
177 return ToSymbolOrErr
.takeError();
178 FixupValue
-= ToSymbol
->getAddress();
181 MachOX86RelocationKind DeltaKind
;
182 Symbol
*TargetSymbol
;
184 if (&BlockToFix
== &FromSymbol
->getAddressable()) {
185 TargetSymbol
= ToSymbol
;
186 DeltaKind
= (SubRI
.r_length
== 3) ? Delta64
: Delta32
;
187 Addend
= FixupValue
+ (FixupAddress
- FromSymbol
->getAddress());
188 // FIXME: handle extern 'from'.
189 } else if (&BlockToFix
== &ToSymbol
->getAddressable()) {
190 TargetSymbol
= FromSymbol
;
191 DeltaKind
= (SubRI
.r_length
== 3) ? NegDelta64
: NegDelta32
;
192 Addend
= FixupValue
- (FixupAddress
- ToSymbol
->getAddress());
194 // BlockToFix was neither FromSymbol nor ToSymbol.
195 return make_error
<JITLinkError
>("SUBTRACTOR relocation must fix up "
196 "either 'A' or 'B' (or a symbol in one "
197 "of their alt-entry chains)");
200 return PairRelocInfo(DeltaKind
, TargetSymbol
, Addend
);
203 Error
addRelocations() override
{
204 using namespace support
;
205 auto &Obj
= getObject();
207 for (auto &S
: Obj
.sections()) {
209 JITTargetAddress SectionAddress
= S
.getAddress();
212 if (S
.relocation_begin() != S
.relocation_end())
213 return make_error
<JITLinkError
>("Virtual section contains "
218 for (auto RelItr
= S
.relocation_begin(), RelEnd
= S
.relocation_end();
219 RelItr
!= RelEnd
; ++RelItr
) {
221 MachO::relocation_info RI
= getRelocationInfo(RelItr
);
223 // Sanity check the relocation kind.
224 auto Kind
= getRelocationKind(RI
);
226 return Kind
.takeError();
228 // Find the address of the value to fix up.
229 JITTargetAddress FixupAddress
= SectionAddress
+ (uint32_t)RI
.r_address
;
232 dbgs() << "Processing relocation at "
233 << format("0x%016" PRIx64
, FixupAddress
) << "\n";
236 // Find the block that the fixup points to.
237 Block
*BlockToFix
= nullptr;
239 auto SymbolToFixOrErr
= findSymbolByAddress(FixupAddress
);
240 if (!SymbolToFixOrErr
)
241 return SymbolToFixOrErr
.takeError();
242 BlockToFix
= &SymbolToFixOrErr
->getBlock();
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");
250 // Get a pointer to the fixup content.
251 const char *FixupContent
= BlockToFix
->getContent().data() +
252 (FixupAddress
- BlockToFix
->getAddress());
254 // The target symbol and addend will be populated by the switch below.
255 Symbol
*TargetSymbol
= nullptr;
263 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
264 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
266 return TargetSymbolOrErr
.takeError();
267 Addend
= *(const ulittle32_t
*)FixupContent
;
270 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
271 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
273 return TargetSymbolOrErr
.takeError();
274 Addend
= *(const ulittle32_t
*)FixupContent
;
277 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
278 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
280 return TargetSymbolOrErr
.takeError();
281 Addend
= *(const ulittle64_t
*)FixupContent
;
283 case Pointer64Anon
: {
284 JITTargetAddress TargetAddress
= *(const ulittle64_t
*)FixupContent
;
285 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
286 TargetSymbol
= &*TargetSymbolOrErr
;
288 return TargetSymbolOrErr
.takeError();
289 Addend
= TargetAddress
- TargetSymbol
->getAddress();
295 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
296 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
298 return TargetSymbolOrErr
.takeError();
299 Addend
= *(const ulittle32_t
*)FixupContent
+
300 (1 << (*Kind
- PCRel32Minus1
));
303 JITTargetAddress TargetAddress
=
304 FixupAddress
+ 4 + *(const ulittle32_t
*)FixupContent
;
305 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
306 TargetSymbol
= &*TargetSymbolOrErr
;
308 return TargetSymbolOrErr
.takeError();
309 Addend
= TargetAddress
- TargetSymbol
->getAddress();
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 ulittle32_t
*)FixupContent
;
319 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
320 TargetSymbol
= &*TargetSymbolOrErr
;
322 return TargetSymbolOrErr
.takeError();
323 Addend
= TargetAddress
- TargetSymbol
->getAddress();
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.
334 parsePairRelocation(*BlockToFix
, *Kind
, RI
, FixupAddress
,
335 FixupContent
, ++RelItr
, RelEnd
);
337 return PairInfo
.takeError();
338 std::tie(*Kind
, TargetSymbol
, Addend
) = *PairInfo
;
339 assert(TargetSymbol
&& "No target symbol from parsePairRelocation?");
343 llvm_unreachable("Special relocation kind should not appear in "
348 Edge
GE(*Kind
, FixupAddress
- BlockToFix
->getAddress(), *TargetSymbol
,
350 printEdge(dbgs(), *BlockToFix
, GE
,
351 getMachOX86RelocationKindName(*Kind
));
354 BlockToFix
->addEdge(*Kind
, FixupAddress
- BlockToFix
->getAddress(),
355 *TargetSymbol
, Addend
);
358 return Error::success();
362 class MachO_x86_64_GOTAndStubsBuilder
363 : public BasicGOTAndStubsBuilder
<MachO_x86_64_GOTAndStubsBuilder
> {
365 MachO_x86_64_GOTAndStubsBuilder(LinkGraph
&G
)
366 : BasicGOTAndStubsBuilder
<MachO_x86_64_GOTAndStubsBuilder
>(G
) {}
368 bool isGOTEdge(Edge
&E
) const {
369 return E
.getKind() == PCRel32GOT
|| E
.getKind() == PCRel32GOTLoad
;
372 Symbol
&createGOTEntry(Symbol
&Target
) {
373 auto &GOTEntryBlock
= G
.createContentBlock(
374 getGOTSection(), getGOTEntryBlockContent(), 0, 8, 0);
375 GOTEntryBlock
.addEdge(Pointer64
, 0, Target
, 0);
376 return G
.addAnonymousSymbol(GOTEntryBlock
, 0, 8, false, false);
379 void fixGOTEdge(Edge
&E
, Symbol
&GOTEntry
) {
380 assert((E
.getKind() == PCRel32GOT
|| E
.getKind() == PCRel32GOTLoad
) &&
383 E
.setTarget(GOTEntry
);
384 // Leave the edge addend as-is.
387 bool isExternalBranchEdge(Edge
&E
) {
388 return E
.getKind() == Branch32
&& !E
.getTarget().isDefined();
391 Symbol
&createStub(Symbol
&Target
) {
392 auto &StubContentBlock
=
393 G
.createContentBlock(getStubsSection(), getStubBlockContent(), 0, 1, 0);
394 // Re-use GOT entries for stub targets.
395 auto &GOTEntrySymbol
= getGOTEntrySymbol(Target
);
396 StubContentBlock
.addEdge(PCRel32
, 2, GOTEntrySymbol
, 0);
397 return G
.addAnonymousSymbol(StubContentBlock
, 0, 6, true, false);
400 void fixExternalBranchEdge(Edge
&E
, Symbol
&Stub
) {
401 assert(E
.getKind() == Branch32
&& "Not a Branch32 edge?");
402 assert(E
.getAddend() == 0 && "Branch32 edge has non-zero addend?");
407 Section
&getGOTSection() {
409 GOTSection
= &G
.createSection("$__GOT", sys::Memory::MF_READ
);
413 Section
&getStubsSection() {
415 auto StubsProt
= static_cast<sys::Memory::ProtectionFlags
>(
416 sys::Memory::MF_READ
| sys::Memory::MF_EXEC
);
417 StubsSection
= &G
.createSection("$__STUBS", StubsProt
);
419 return *StubsSection
;
422 StringRef
getGOTEntryBlockContent() {
423 return StringRef(reinterpret_cast<const char *>(NullGOTEntryContent
),
424 sizeof(NullGOTEntryContent
));
427 StringRef
getStubBlockContent() {
428 return StringRef(reinterpret_cast<const char *>(StubContent
),
429 sizeof(StubContent
));
432 static const uint8_t NullGOTEntryContent
[8];
433 static const uint8_t StubContent
[6];
434 Section
*GOTSection
= nullptr;
435 Section
*StubsSection
= nullptr;
438 const uint8_t MachO_x86_64_GOTAndStubsBuilder::NullGOTEntryContent
[8] = {
439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
440 const uint8_t MachO_x86_64_GOTAndStubsBuilder::StubContent
[6] = {
441 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
447 class MachOJITLinker_x86_64
: public JITLinker
<MachOJITLinker_x86_64
> {
448 friend class JITLinker
<MachOJITLinker_x86_64
>;
451 MachOJITLinker_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
,
452 PassConfiguration PassConfig
)
453 : JITLinker(std::move(Ctx
), std::move(PassConfig
)) {}
456 StringRef
getEdgeKindName(Edge::Kind R
) const override
{
457 return getMachOX86RelocationKindName(R
);
460 Expected
<std::unique_ptr
<LinkGraph
>>
461 buildGraph(MemoryBufferRef ObjBuffer
) override
{
462 auto MachOObj
= object::ObjectFile::createMachOObjectFile(ObjBuffer
);
464 return MachOObj
.takeError();
465 return MachOLinkGraphBuilder_x86_64(**MachOObj
).buildGraph();
468 static Error
targetOutOfRangeError(const Block
&B
, const Edge
&E
) {
471 raw_string_ostream
ErrStream(ErrMsg
);
472 ErrStream
<< "Relocation target out of range: ";
473 printEdge(ErrStream
, B
, E
, getMachOX86RelocationKindName(E
.getKind()));
476 return make_error
<JITLinkError
>(std::move(ErrMsg
));
479 Error
applyFixup(Block
&B
, const Edge
&E
, char *BlockWorkingMem
) const {
481 using namespace support
;
483 char *FixupPtr
= BlockWorkingMem
+ E
.getOffset();
484 JITTargetAddress FixupAddress
= B
.getAddress() + E
.getOffset();
486 switch (E
.getKind()) {
491 E
.getTarget().getAddress() - (FixupAddress
+ 4) + E
.getAddend();
492 if (Value
< std::numeric_limits
<int32_t>::min() ||
493 Value
> std::numeric_limits
<int32_t>::max())
494 return targetOutOfRangeError(B
, E
);
495 *(little32_t
*)FixupPtr
= Value
;
499 case Pointer64Anon
: {
500 uint64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
501 *(ulittle64_t
*)FixupPtr
= Value
;
506 case PCRel32Minus4
: {
507 int Delta
= 4 + (1 << (E
.getKind() - PCRel32Minus1
));
509 E
.getTarget().getAddress() - (FixupAddress
+ Delta
) + E
.getAddend();
510 if (Value
< std::numeric_limits
<int32_t>::min() ||
511 Value
> std::numeric_limits
<int32_t>::max())
512 return targetOutOfRangeError(B
, E
);
513 *(little32_t
*)FixupPtr
= Value
;
516 case PCRel32Minus1Anon
:
517 case PCRel32Minus2Anon
:
518 case PCRel32Minus4Anon
: {
519 int Delta
= 4 + (1 << (E
.getKind() - PCRel32Minus1Anon
));
521 E
.getTarget().getAddress() - (FixupAddress
+ Delta
) + E
.getAddend();
522 if (Value
< std::numeric_limits
<int32_t>::min() ||
523 Value
> std::numeric_limits
<int32_t>::max())
524 return targetOutOfRangeError(B
, E
);
525 *(little32_t
*)FixupPtr
= Value
;
533 if (E
.getKind() == Delta32
|| E
.getKind() == Delta64
)
534 Value
= E
.getTarget().getAddress() - FixupAddress
+ E
.getAddend();
536 Value
= FixupAddress
- E
.getTarget().getAddress() + E
.getAddend();
538 if (E
.getKind() == Delta32
|| E
.getKind() == NegDelta32
) {
539 if (Value
< std::numeric_limits
<int32_t>::min() ||
540 Value
> std::numeric_limits
<int32_t>::max())
541 return targetOutOfRangeError(B
, E
);
542 *(little32_t
*)FixupPtr
= Value
;
544 *(little64_t
*)FixupPtr
= Value
;
548 uint64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
549 if (Value
> std::numeric_limits
<uint32_t>::max())
550 return targetOutOfRangeError(B
, E
);
551 *(ulittle32_t
*)FixupPtr
= Value
;
555 llvm_unreachable("Unrecognized edge kind");
558 return Error::success();
561 uint64_t NullValue
= 0;
564 void jitLink_MachO_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
) {
565 PassConfiguration Config
;
566 Triple
TT("x86_64-apple-macosx");
568 if (Ctx
->shouldAddDefaultTargetPasses(TT
)) {
569 // Add a mark-live pass.
570 if (auto MarkLive
= Ctx
->getMarkLivePass(TT
))
571 Config
.PrePrunePasses
.push_back(std::move(MarkLive
));
573 Config
.PrePrunePasses
.push_back(markAllSymbolsLive
);
575 // Add an in-place GOT/Stubs pass.
576 Config
.PostPrunePasses
.push_back([](LinkGraph
&G
) -> Error
{
577 MachO_x86_64_GOTAndStubsBuilder(G
).run();
578 return Error::success();
582 if (auto Err
= Ctx
->modifyPassConfig(TT
, Config
))
583 return Ctx
->notifyFailed(std::move(Err
));
585 // Construct a JITLinker and run the link function.
586 MachOJITLinker_x86_64::link(std::move(Ctx
), std::move(Config
));
589 StringRef
getMachOX86RelocationKindName(Edge::Kind R
) {
598 return "Pointer64Anon";
602 return "PCRel32Minus1";
604 return "PCRel32Minus2";
606 return "PCRel32Minus4";
608 return "PCRel32Anon";
609 case PCRel32Minus1Anon
:
610 return "PCRel32Minus1Anon";
611 case PCRel32Minus2Anon
:
612 return "PCRel32Minus2Anon";
613 case PCRel32Minus4Anon
:
614 return "PCRel32Minus4Anon";
616 return "PCRel32GOTLoad";
630 return getGenericEdgeKindName(static_cast<Edge::Kind
>(R
));
634 } // end namespace jitlink
635 } // end namespace llvm