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 "MachOAtomGraphBuilder.h"
18 #define DEBUG_TYPE "jitlink"
21 using namespace llvm::jitlink
;
22 using namespace llvm::jitlink::MachO_x86_64_Edges
;
26 class MachOAtomGraphBuilder_x86_64
: public MachOAtomGraphBuilder
{
28 MachOAtomGraphBuilder_x86_64(const object::MachOObjectFile
&Obj
)
29 : MachOAtomGraphBuilder(Obj
),
30 NumSymbols(Obj
.getSymtabLoadCommand().nsyms
) {
31 addCustomAtomizer("__eh_frame", [this](MachOSection
&EHFrameSection
) {
32 return addEHFrame(getGraph(), EHFrameSection
.getGenericSection(),
33 EHFrameSection
.getContent(),
34 EHFrameSection
.getAddress(), NegDelta32
, Delta64
);
39 static Expected
<MachOX86RelocationKind
>
40 getRelocationKind(const MachO::relocation_info
&RI
) {
42 case MachO::X86_64_RELOC_UNSIGNED
:
45 return RI
.r_extern
? Pointer64
: Pointer64Anon
;
46 else if (RI
.r_extern
&& RI
.r_length
== 2)
50 case MachO::X86_64_RELOC_SIGNED
:
51 if (RI
.r_pcrel
&& RI
.r_length
== 2)
52 return RI
.r_extern
? PCRel32
: PCRel32Anon
;
54 case MachO::X86_64_RELOC_BRANCH
:
55 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
58 case MachO::X86_64_RELOC_GOT_LOAD
:
59 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
60 return PCRel32GOTLoad
;
62 case MachO::X86_64_RELOC_GOT
:
63 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
66 case MachO::X86_64_RELOC_SUBTRACTOR
:
67 // SUBTRACTOR must be non-pc-rel, extern, with length 2 or 3.
68 // Initially represent SUBTRACTOR relocations with 'Delta<W>'. They may
69 // be turned into NegDelta<W> by parsePairRelocation.
70 if (!RI
.r_pcrel
&& RI
.r_extern
) {
73 else if (RI
.r_length
== 3)
77 case MachO::X86_64_RELOC_SIGNED_1
:
78 if (RI
.r_pcrel
&& RI
.r_length
== 2)
79 return RI
.r_extern
? PCRel32Minus1
: PCRel32Minus1Anon
;
81 case MachO::X86_64_RELOC_SIGNED_2
:
82 if (RI
.r_pcrel
&& RI
.r_length
== 2)
83 return RI
.r_extern
? PCRel32Minus2
: PCRel32Minus2Anon
;
85 case MachO::X86_64_RELOC_SIGNED_4
:
86 if (RI
.r_pcrel
&& RI
.r_length
== 2)
87 return RI
.r_extern
? PCRel32Minus4
: PCRel32Minus4Anon
;
89 case MachO::X86_64_RELOC_TLV
:
90 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
95 return make_error
<JITLinkError
>(
96 "Unsupported x86-64 relocation: address=" +
97 formatv("{0:x8}", RI
.r_address
) +
98 ", symbolnum=" + formatv("{0:x6}", RI
.r_symbolnum
) +
99 ", kind=" + formatv("{0:x1}", RI
.r_type
) +
100 ", pc_rel=" + (RI
.r_pcrel
? "true" : "false") +
101 ", extern=" + (RI
.r_extern
? "true" : "false") +
102 ", length=" + formatv("{0:d}", RI
.r_length
));
105 Expected
<Atom
&> findAtomBySymbolIndex(const MachO::relocation_info
&RI
) {
106 auto &Obj
= getObject();
107 if (RI
.r_symbolnum
>= NumSymbols
)
108 return make_error
<JITLinkError
>("Symbol index out of range");
109 auto SymI
= Obj
.getSymbolByIndex(RI
.r_symbolnum
);
110 auto Name
= SymI
->getName();
112 return Name
.takeError();
113 return getGraph().getAtomByName(*Name
);
116 MachO::relocation_info
117 getRelocationInfo(const object::relocation_iterator RelItr
) {
118 MachO::any_relocation_info ARI
=
119 getObject().getRelocation(RelItr
->getRawDataRefImpl());
120 MachO::relocation_info RI
;
121 memcpy(&RI
, &ARI
, sizeof(MachO::relocation_info
));
125 using PairRelocInfo
= std::tuple
<MachOX86RelocationKind
, Atom
*, uint64_t>;
127 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
128 // returns the edge kind and addend to be used.
129 Expected
<PairRelocInfo
>
130 parsePairRelocation(DefinedAtom
&AtomToFix
, Edge::Kind SubtractorKind
,
131 const MachO::relocation_info
&SubRI
,
132 JITTargetAddress FixupAddress
, const char *FixupContent
,
133 object::relocation_iterator
&UnsignedRelItr
,
134 object::relocation_iterator
&RelEnd
) {
135 using namespace support
;
137 assert(((SubtractorKind
== Delta32
&& SubRI
.r_length
== 2) ||
138 (SubtractorKind
== Delta64
&& SubRI
.r_length
== 3)) &&
139 "Subtractor kind should match length");
140 assert(SubRI
.r_extern
&& "SUBTRACTOR reloc symbol should be extern");
141 assert(!SubRI
.r_pcrel
&& "SUBTRACTOR reloc should not be PCRel");
143 if (UnsignedRelItr
== RelEnd
)
144 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR without paired "
145 "UNSIGNED relocation");
147 auto UnsignedRI
= getRelocationInfo(UnsignedRelItr
);
149 if (SubRI
.r_address
!= UnsignedRI
.r_address
)
150 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR and paired UNSIGNED "
151 "point to different addresses");
153 if (SubRI
.r_length
!= UnsignedRI
.r_length
)
154 return make_error
<JITLinkError
>("length of x86_64 SUBTRACTOR and paired "
155 "UNSIGNED reloc must match");
157 auto FromAtom
= findAtomBySymbolIndex(SubRI
);
159 return FromAtom
.takeError();
161 // Read the current fixup value.
162 uint64_t FixupValue
= 0;
163 if (SubRI
.r_length
== 3)
164 FixupValue
= *(const little64_t
*)FixupContent
;
166 FixupValue
= *(const little32_t
*)FixupContent
;
168 // Find 'ToAtom' using symbol number or address, depending on whether the
169 // paired UNSIGNED relocation is extern.
170 Atom
*ToAtom
= nullptr;
171 if (UnsignedRI
.r_extern
) {
172 // Find target atom by symbol index.
173 if (auto ToAtomOrErr
= findAtomBySymbolIndex(UnsignedRI
))
174 ToAtom
= &*ToAtomOrErr
;
176 return ToAtomOrErr
.takeError();
178 if (auto ToAtomOrErr
= getGraph().findAtomByAddress(FixupValue
))
179 ToAtom
= &*ToAtomOrErr
;
181 return ToAtomOrErr
.takeError();
182 FixupValue
-= ToAtom
->getAddress();
185 MachOX86RelocationKind DeltaKind
;
188 if (areLayoutLocked(AtomToFix
, *FromAtom
)) {
190 DeltaKind
= (SubRI
.r_length
== 3) ? Delta64
: Delta32
;
191 Addend
= FixupValue
+ (FixupAddress
- FromAtom
->getAddress());
192 // FIXME: handle extern 'from'.
193 } else if (areLayoutLocked(AtomToFix
, *ToAtom
)) {
194 TargetAtom
= &*FromAtom
;
195 DeltaKind
= (SubRI
.r_length
== 3) ? NegDelta64
: NegDelta32
;
196 Addend
= FixupValue
- (FixupAddress
- ToAtom
->getAddress());
198 // AtomToFix was neither FromAtom nor ToAtom.
199 return make_error
<JITLinkError
>("SUBTRACTOR relocation must fix up "
200 "either 'A' or 'B' (or an atom in one "
201 "of their alt-entry groups)");
204 return PairRelocInfo(DeltaKind
, TargetAtom
, Addend
);
207 Error
addRelocations() override
{
208 using namespace support
;
209 auto &G
= getGraph();
210 auto &Obj
= getObject();
212 for (auto &S
: Obj
.sections()) {
214 JITTargetAddress SectionAddress
= S
.getAddress();
216 for (auto RelItr
= S
.relocation_begin(), RelEnd
= S
.relocation_end();
217 RelItr
!= RelEnd
; ++RelItr
) {
219 MachO::relocation_info RI
= getRelocationInfo(RelItr
);
221 // Sanity check the relocation kind.
222 auto Kind
= getRelocationKind(RI
);
224 return Kind
.takeError();
226 // Find the address of the value to fix up.
227 JITTargetAddress FixupAddress
= SectionAddress
+ (uint32_t)RI
.r_address
;
230 dbgs() << "Processing relocation at "
231 << format("0x%016" PRIx64
, FixupAddress
) << "\n";
234 // Find the atom that the fixup points to.
235 DefinedAtom
*AtomToFix
= nullptr;
237 auto AtomToFixOrErr
= G
.findAtomByAddress(FixupAddress
);
239 return AtomToFixOrErr
.takeError();
240 AtomToFix
= &*AtomToFixOrErr
;
243 if (FixupAddress
+ static_cast<JITTargetAddress
>(1ULL << RI
.r_length
) >
244 AtomToFix
->getAddress() + AtomToFix
->getContent().size())
245 return make_error
<JITLinkError
>(
246 "Relocation content extends past end of fixup atom");
248 // Get a pointer to the fixup content.
249 const char *FixupContent
= AtomToFix
->getContent().data() +
250 (FixupAddress
- AtomToFix
->getAddress());
252 // The target atom and addend will be populated by the switch below.
253 Atom
*TargetAtom
= nullptr;
261 if (auto TargetAtomOrErr
= findAtomBySymbolIndex(RI
))
262 TargetAtom
= &*TargetAtomOrErr
;
264 return TargetAtomOrErr
.takeError();
265 Addend
= *(const ulittle32_t
*)FixupContent
;
268 if (auto TargetAtomOrErr
= findAtomBySymbolIndex(RI
))
269 TargetAtom
= &*TargetAtomOrErr
;
271 return TargetAtomOrErr
.takeError();
272 Addend
= *(const ulittle32_t
*)FixupContent
;
275 if (auto TargetAtomOrErr
= findAtomBySymbolIndex(RI
))
276 TargetAtom
= &*TargetAtomOrErr
;
278 return TargetAtomOrErr
.takeError();
279 Addend
= *(const ulittle64_t
*)FixupContent
;
281 case Pointer64Anon
: {
282 JITTargetAddress TargetAddress
= *(const ulittle64_t
*)FixupContent
;
283 if (auto TargetAtomOrErr
= G
.findAtomByAddress(TargetAddress
))
284 TargetAtom
= &*TargetAtomOrErr
;
286 return TargetAtomOrErr
.takeError();
287 Addend
= TargetAddress
- TargetAtom
->getAddress();
293 if (auto TargetAtomOrErr
= findAtomBySymbolIndex(RI
))
294 TargetAtom
= &*TargetAtomOrErr
;
296 return TargetAtomOrErr
.takeError();
297 Addend
= *(const ulittle32_t
*)FixupContent
+
298 (1 << (*Kind
- PCRel32Minus1
));
301 JITTargetAddress TargetAddress
=
302 FixupAddress
+ 4 + *(const ulittle32_t
*)FixupContent
;
303 if (auto TargetAtomOrErr
= G
.findAtomByAddress(TargetAddress
))
304 TargetAtom
= &*TargetAtomOrErr
;
306 return TargetAtomOrErr
.takeError();
307 Addend
= TargetAddress
- TargetAtom
->getAddress();
310 case PCRel32Minus1Anon
:
311 case PCRel32Minus2Anon
:
312 case PCRel32Minus4Anon
: {
313 JITTargetAddress Delta
=
314 static_cast<JITTargetAddress
>(1ULL << (*Kind
- PCRel32Minus1Anon
));
315 JITTargetAddress TargetAddress
=
316 FixupAddress
+ 4 + Delta
+ *(const ulittle32_t
*)FixupContent
;
317 if (auto TargetAtomOrErr
= G
.findAtomByAddress(TargetAddress
))
318 TargetAtom
= &*TargetAtomOrErr
;
320 return TargetAtomOrErr
.takeError();
321 Addend
= TargetAddress
- TargetAtom
->getAddress();
326 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
327 // parsePairRelocation handles the paired reloc, and returns the
328 // edge kind to be used (either Delta32/Delta64, or
329 // NegDelta32/NegDelta64, depending on the direction of the
330 // subtraction) along with the addend.
332 parsePairRelocation(*AtomToFix
, *Kind
, RI
, FixupAddress
,
333 FixupContent
, ++RelItr
, RelEnd
);
335 return PairInfo
.takeError();
336 std::tie(*Kind
, TargetAtom
, Addend
) = *PairInfo
;
337 assert(TargetAtom
&& "No target atom from parsePairRelocation?");
341 llvm_unreachable("Special relocation kind should not appear in "
346 Edge
GE(*Kind
, FixupAddress
- AtomToFix
->getAddress(), *TargetAtom
,
348 printEdge(dbgs(), *AtomToFix
, GE
,
349 getMachOX86RelocationKindName(*Kind
));
352 AtomToFix
->addEdge(*Kind
, FixupAddress
- AtomToFix
->getAddress(),
353 *TargetAtom
, Addend
);
356 return Error::success();
359 unsigned NumSymbols
= 0;
362 class MachO_x86_64_GOTAndStubsBuilder
363 : public BasicGOTAndStubsBuilder
<MachO_x86_64_GOTAndStubsBuilder
> {
365 MachO_x86_64_GOTAndStubsBuilder(AtomGraph
&G
)
366 : BasicGOTAndStubsBuilder
<MachO_x86_64_GOTAndStubsBuilder
>(G
) {}
368 bool isGOTEdge(Edge
&E
) const {
369 return E
.getKind() == PCRel32GOT
|| E
.getKind() == PCRel32GOTLoad
;
372 DefinedAtom
&createGOTEntry(Atom
&Target
) {
373 auto &GOTEntryAtom
= G
.addAnonymousAtom(getGOTSection(), 0x0, 8);
374 GOTEntryAtom
.setContent(
375 StringRef(reinterpret_cast<const char *>(NullGOTEntryContent
), 8));
376 GOTEntryAtom
.addEdge(Pointer64
, 0, Target
, 0);
380 void fixGOTEdge(Edge
&E
, Atom
&GOTEntry
) {
381 assert((E
.getKind() == PCRel32GOT
|| E
.getKind() == PCRel32GOTLoad
) &&
384 E
.setTarget(GOTEntry
);
385 // Leave the edge addend as-is.
388 bool isExternalBranchEdge(Edge
&E
) {
389 return E
.getKind() == Branch32
&& !E
.getTarget().isDefined();
392 DefinedAtom
&createStub(Atom
&Target
) {
393 auto &StubAtom
= G
.addAnonymousAtom(getStubsSection(), 0x0, 2);
395 StringRef(reinterpret_cast<const char *>(StubContent
), 6));
397 // Re-use GOT entries for stub targets.
398 auto &GOTEntryAtom
= getGOTEntryAtom(Target
);
399 StubAtom
.addEdge(PCRel32
, 2, GOTEntryAtom
, 0);
404 void fixExternalBranchEdge(Edge
&E
, Atom
&Stub
) {
405 assert(E
.getKind() == Branch32
&& "Not a Branch32 edge?");
406 assert(E
.getAddend() == 0 && "Branch32 edge has non-zero addend?");
411 Section
&getGOTSection() {
413 GOTSection
= &G
.createSection("$__GOT", 8, sys::Memory::MF_READ
, false);
417 Section
&getStubsSection() {
419 auto StubsProt
= static_cast<sys::Memory::ProtectionFlags
>(
420 sys::Memory::MF_READ
| sys::Memory::MF_EXEC
);
421 StubsSection
= &G
.createSection("$__STUBS", 8, StubsProt
, false);
423 return *StubsSection
;
426 static const uint8_t NullGOTEntryContent
[8];
427 static const uint8_t StubContent
[6];
428 Section
*GOTSection
= nullptr;
429 Section
*StubsSection
= nullptr;
432 const uint8_t MachO_x86_64_GOTAndStubsBuilder::NullGOTEntryContent
[8] = {
433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
434 const uint8_t MachO_x86_64_GOTAndStubsBuilder::StubContent
[6] = {
435 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
441 class MachOJITLinker_x86_64
: public JITLinker
<MachOJITLinker_x86_64
> {
442 friend class JITLinker
<MachOJITLinker_x86_64
>;
445 MachOJITLinker_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
,
446 PassConfiguration PassConfig
)
447 : JITLinker(std::move(Ctx
), std::move(PassConfig
)) {}
450 StringRef
getEdgeKindName(Edge::Kind R
) const override
{
451 return getMachOX86RelocationKindName(R
);
454 Expected
<std::unique_ptr
<AtomGraph
>>
455 buildGraph(MemoryBufferRef ObjBuffer
) override
{
456 auto MachOObj
= object::ObjectFile::createMachOObjectFile(ObjBuffer
);
458 return MachOObj
.takeError();
459 return MachOAtomGraphBuilder_x86_64(**MachOObj
).buildGraph();
462 static Error
targetOutOfRangeError(const Atom
&A
, const Edge
&E
) {
465 raw_string_ostream
ErrStream(ErrMsg
);
466 ErrStream
<< "Relocation target out of range: ";
467 printEdge(ErrStream
, A
, E
, getMachOX86RelocationKindName(E
.getKind()));
470 return make_error
<JITLinkError
>(std::move(ErrMsg
));
473 Error
applyFixup(DefinedAtom
&A
, const Edge
&E
, char *AtomWorkingMem
) const {
474 using namespace support
;
476 char *FixupPtr
= AtomWorkingMem
+ E
.getOffset();
477 JITTargetAddress FixupAddress
= A
.getAddress() + E
.getOffset();
479 switch (E
.getKind()) {
484 E
.getTarget().getAddress() - (FixupAddress
+ 4) + E
.getAddend();
485 if (Value
< std::numeric_limits
<int32_t>::min() ||
486 Value
> std::numeric_limits
<int32_t>::max())
487 return targetOutOfRangeError(A
, E
);
488 *(little32_t
*)FixupPtr
= Value
;
492 case Pointer64Anon
: {
493 uint64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
494 *(ulittle64_t
*)FixupPtr
= Value
;
499 case PCRel32Minus4
: {
500 int Delta
= 4 + (1 << (E
.getKind() - PCRel32Minus1
));
502 E
.getTarget().getAddress() - (FixupAddress
+ Delta
) + E
.getAddend();
503 if (Value
< std::numeric_limits
<int32_t>::min() ||
504 Value
> std::numeric_limits
<int32_t>::max())
505 return targetOutOfRangeError(A
, E
);
506 *(little32_t
*)FixupPtr
= Value
;
509 case PCRel32Minus1Anon
:
510 case PCRel32Minus2Anon
:
511 case PCRel32Minus4Anon
: {
512 int Delta
= 4 + (1 << (E
.getKind() - PCRel32Minus1Anon
));
514 E
.getTarget().getAddress() - (FixupAddress
+ Delta
) + E
.getAddend();
515 if (Value
< std::numeric_limits
<int32_t>::min() ||
516 Value
> std::numeric_limits
<int32_t>::max())
517 return targetOutOfRangeError(A
, E
);
518 *(little32_t
*)FixupPtr
= Value
;
526 if (E
.getKind() == Delta32
|| E
.getKind() == Delta64
)
527 Value
= E
.getTarget().getAddress() - FixupAddress
+ E
.getAddend();
529 Value
= FixupAddress
- E
.getTarget().getAddress() + E
.getAddend();
531 if (E
.getKind() == Delta32
|| E
.getKind() == NegDelta32
) {
532 if (Value
< std::numeric_limits
<int32_t>::min() ||
533 Value
> std::numeric_limits
<int32_t>::max())
534 return targetOutOfRangeError(A
, E
);
535 *(little32_t
*)FixupPtr
= Value
;
537 *(little64_t
*)FixupPtr
= Value
;
541 uint64_t Value
= E
.getTarget().getAddress() + E
.getAddend();
542 if (Value
> std::numeric_limits
<uint32_t>::max())
543 return targetOutOfRangeError(A
, E
);
544 *(ulittle32_t
*)FixupPtr
= Value
;
548 llvm_unreachable("Unrecognized edge kind");
551 return Error::success();
554 uint64_t NullValue
= 0;
557 void jitLink_MachO_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
) {
558 PassConfiguration Config
;
559 Triple
TT("x86_64-apple-macosx");
561 if (Ctx
->shouldAddDefaultTargetPasses(TT
)) {
562 // Add a mark-live pass.
563 if (auto MarkLive
= Ctx
->getMarkLivePass(TT
))
564 Config
.PrePrunePasses
.push_back(std::move(MarkLive
));
566 Config
.PrePrunePasses
.push_back(markAllAtomsLive
);
568 // Add an in-place GOT/Stubs pass.
569 Config
.PostPrunePasses
.push_back([](AtomGraph
&G
) -> Error
{
570 MachO_x86_64_GOTAndStubsBuilder(G
).run();
571 return Error::success();
575 if (auto Err
= Ctx
->modifyPassConfig(TT
, Config
))
576 return Ctx
->notifyFailed(std::move(Err
));
578 // Construct a JITLinker and run the link function.
579 MachOJITLinker_x86_64::link(std::move(Ctx
), std::move(Config
));
582 StringRef
getMachOX86RelocationKindName(Edge::Kind R
) {
591 return "Pointer64Anon";
595 return "PCRel32Minus1";
597 return "PCRel32Minus2";
599 return "PCRel32Minus4";
601 return "PCRel32Anon";
602 case PCRel32Minus1Anon
:
603 return "PCRel32Minus1Anon";
604 case PCRel32Minus2Anon
:
605 return "PCRel32Minus2Anon";
606 case PCRel32Minus4Anon
:
607 return "PCRel32Minus4Anon";
609 return "PCRel32GOTLoad";
623 return getGenericEdgeKindName(static_cast<Edge::Kind
>(R
));
627 } // end namespace jitlink
628 } // end namespace llvm