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"
14 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
16 #include "MachOLinkGraphBuilder.h"
17 #include "PerGraphGOTAndPLTStubsBuilder.h"
19 #define DEBUG_TYPE "jitlink"
22 using namespace llvm::jitlink
;
26 class MachOLinkGraphBuilder_x86_64
: public MachOLinkGraphBuilder
{
28 MachOLinkGraphBuilder_x86_64(const object::MachOObjectFile
&Obj
)
29 : MachOLinkGraphBuilder(Obj
, Triple("x86_64-apple-darwin"),
30 x86_64::getEdgeKindName
) {}
33 enum MachONormalizedRelocationType
: unsigned {
43 MachOPCRel32Minus1Anon
,
44 MachOPCRel32Minus2Anon
,
45 MachOPCRel32Minus4Anon
,
53 static Expected
<MachONormalizedRelocationType
>
54 getRelocKind(const MachO::relocation_info
&RI
) {
56 case MachO::X86_64_RELOC_UNSIGNED
:
59 return RI
.r_extern
? MachOPointer64
: MachOPointer64Anon
;
60 else if (RI
.r_extern
&& RI
.r_length
== 2)
61 return MachOPointer32
;
64 case MachO::X86_64_RELOC_SIGNED
:
65 if (RI
.r_pcrel
&& RI
.r_length
== 2)
66 return RI
.r_extern
? MachOPCRel32
: MachOPCRel32Anon
;
68 case MachO::X86_64_RELOC_BRANCH
:
69 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
72 case MachO::X86_64_RELOC_GOT_LOAD
:
73 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
74 return MachOPCRel32GOTLoad
;
76 case MachO::X86_64_RELOC_GOT
:
77 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
78 return MachOPCRel32GOT
;
80 case MachO::X86_64_RELOC_SUBTRACTOR
:
81 if (!RI
.r_pcrel
&& RI
.r_extern
) {
83 return MachOSubtractor32
;
84 else if (RI
.r_length
== 3)
85 return MachOSubtractor64
;
88 case MachO::X86_64_RELOC_SIGNED_1
:
89 if (RI
.r_pcrel
&& RI
.r_length
== 2)
90 return RI
.r_extern
? MachOPCRel32Minus1
: MachOPCRel32Minus1Anon
;
92 case MachO::X86_64_RELOC_SIGNED_2
:
93 if (RI
.r_pcrel
&& RI
.r_length
== 2)
94 return RI
.r_extern
? MachOPCRel32Minus2
: MachOPCRel32Minus2Anon
;
96 case MachO::X86_64_RELOC_SIGNED_4
:
97 if (RI
.r_pcrel
&& RI
.r_length
== 2)
98 return RI
.r_extern
? MachOPCRel32Minus4
: MachOPCRel32Minus4Anon
;
100 case MachO::X86_64_RELOC_TLV
:
101 if (RI
.r_pcrel
&& RI
.r_extern
&& RI
.r_length
== 2)
102 return MachOPCRel32TLV
;
106 return make_error
<JITLinkError
>(
107 "Unsupported x86-64 relocation: address=" +
108 formatv("{0:x8}", RI
.r_address
) +
109 ", symbolnum=" + formatv("{0:x6}", RI
.r_symbolnum
) +
110 ", kind=" + formatv("{0:x1}", RI
.r_type
) +
111 ", pc_rel=" + (RI
.r_pcrel
? "true" : "false") +
112 ", extern=" + (RI
.r_extern
? "true" : "false") +
113 ", length=" + formatv("{0:d}", RI
.r_length
));
116 using PairRelocInfo
= std::tuple
<Edge::Kind
, Symbol
*, uint64_t>;
118 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
119 // returns the edge kind and addend to be used.
120 Expected
<PairRelocInfo
> parsePairRelocation(
121 Block
&BlockToFix
, MachONormalizedRelocationType SubtractorKind
,
122 const MachO::relocation_info
&SubRI
, JITTargetAddress FixupAddress
,
123 const char *FixupContent
, object::relocation_iterator
&UnsignedRelItr
,
124 object::relocation_iterator
&RelEnd
) {
125 using namespace support
;
127 assert(((SubtractorKind
== MachOSubtractor32
&& SubRI
.r_length
== 2) ||
128 (SubtractorKind
== MachOSubtractor64
&& SubRI
.r_length
== 3)) &&
129 "Subtractor kind should match length");
130 assert(SubRI
.r_extern
&& "SUBTRACTOR reloc symbol should be extern");
131 assert(!SubRI
.r_pcrel
&& "SUBTRACTOR reloc should not be PCRel");
133 if (UnsignedRelItr
== RelEnd
)
134 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR without paired "
135 "UNSIGNED relocation");
137 auto UnsignedRI
= getRelocationInfo(UnsignedRelItr
);
139 if (SubRI
.r_address
!= UnsignedRI
.r_address
)
140 return make_error
<JITLinkError
>("x86_64 SUBTRACTOR and paired UNSIGNED "
141 "point to different addresses");
143 if (SubRI
.r_length
!= UnsignedRI
.r_length
)
144 return make_error
<JITLinkError
>("length of x86_64 SUBTRACTOR and paired "
145 "UNSIGNED reloc must match");
148 if (auto FromSymbolOrErr
= findSymbolByIndex(SubRI
.r_symbolnum
))
149 FromSymbol
= FromSymbolOrErr
->GraphSymbol
;
151 return FromSymbolOrErr
.takeError();
153 // Read the current fixup value.
154 uint64_t FixupValue
= 0;
155 if (SubRI
.r_length
== 3)
156 FixupValue
= *(const little64_t
*)FixupContent
;
158 FixupValue
= *(const little32_t
*)FixupContent
;
160 // Find 'ToSymbol' using symbol number or address, depending on whether the
161 // paired UNSIGNED relocation is extern.
162 Symbol
*ToSymbol
= nullptr;
163 if (UnsignedRI
.r_extern
) {
164 // Find target symbol by symbol index.
165 if (auto ToSymbolOrErr
= findSymbolByIndex(UnsignedRI
.r_symbolnum
))
166 ToSymbol
= ToSymbolOrErr
->GraphSymbol
;
168 return ToSymbolOrErr
.takeError();
170 auto ToSymbolSec
= findSectionByIndex(UnsignedRI
.r_symbolnum
- 1);
172 return ToSymbolSec
.takeError();
173 ToSymbol
= getSymbolByAddress(ToSymbolSec
->Address
);
174 assert(ToSymbol
&& "No symbol for section");
175 FixupValue
-= ToSymbol
->getAddress();
178 Edge::Kind DeltaKind
;
179 Symbol
*TargetSymbol
;
181 if (&BlockToFix
== &FromSymbol
->getAddressable()) {
182 TargetSymbol
= ToSymbol
;
183 DeltaKind
= (SubRI
.r_length
== 3) ? x86_64::Delta64
: x86_64::Delta32
;
184 Addend
= FixupValue
+ (FixupAddress
- FromSymbol
->getAddress());
185 // FIXME: handle extern 'from'.
186 } else if (&BlockToFix
== &ToSymbol
->getAddressable()) {
187 TargetSymbol
= FromSymbol
;
189 (SubRI
.r_length
== 3) ? x86_64::NegDelta64
: x86_64::NegDelta32
;
190 Addend
= FixupValue
- (FixupAddress
- ToSymbol
->getAddress());
192 // BlockToFix was neither FromSymbol nor ToSymbol.
193 return make_error
<JITLinkError
>("SUBTRACTOR relocation must fix up "
194 "either 'A' or 'B' (or a symbol in one "
195 "of their alt-entry chains)");
198 return PairRelocInfo(DeltaKind
, TargetSymbol
, Addend
);
201 Error
addRelocations() override
{
202 using namespace support
;
203 auto &Obj
= getObject();
205 LLVM_DEBUG(dbgs() << "Processing relocations:\n");
207 for (auto &S
: Obj
.sections()) {
209 JITTargetAddress SectionAddress
= S
.getAddress();
211 // Skip relocations virtual sections.
213 if (S
.relocation_begin() != S
.relocation_end())
214 return make_error
<JITLinkError
>("Virtual section contains "
219 // Skip relocations for debug symbols.
222 getSectionByIndex(Obj
.getSectionIndex(S
.getRawDataRefImpl()));
223 if (!NSec
.GraphSection
) {
225 dbgs() << " Skipping relocations for MachO section "
226 << NSec
.SegName
<< "/" << NSec
.SectName
227 << " which has no associated graph section\n";
233 // Add relocations for section.
234 for (auto RelItr
= S
.relocation_begin(), RelEnd
= S
.relocation_end();
235 RelItr
!= RelEnd
; ++RelItr
) {
237 MachO::relocation_info RI
= getRelocationInfo(RelItr
);
239 // Find the address of the value to fix up.
240 JITTargetAddress FixupAddress
= SectionAddress
+ (uint32_t)RI
.r_address
;
244 getSectionByIndex(Obj
.getSectionIndex(S
.getRawDataRefImpl()));
245 dbgs() << " " << NSec
.SectName
<< " + "
246 << formatv("{0:x8}", RI
.r_address
) << ":\n";
249 // Find the block that the fixup points to.
250 Block
*BlockToFix
= nullptr;
252 auto SymbolToFixOrErr
= findSymbolByAddress(FixupAddress
);
253 if (!SymbolToFixOrErr
)
254 return SymbolToFixOrErr
.takeError();
255 BlockToFix
= &SymbolToFixOrErr
->getBlock();
258 if (FixupAddress
+ static_cast<JITTargetAddress
>(1ULL << RI
.r_length
) >
259 BlockToFix
->getAddress() + BlockToFix
->getContent().size())
260 return make_error
<JITLinkError
>(
261 "Relocation extends past end of fixup block");
263 // Get a pointer to the fixup content.
264 const char *FixupContent
= BlockToFix
->getContent().data() +
265 (FixupAddress
- BlockToFix
->getAddress());
267 size_t FixupOffset
= FixupAddress
- BlockToFix
->getAddress();
269 // The target symbol and addend will be populated by the switch below.
270 Symbol
*TargetSymbol
= nullptr;
273 // Sanity check the relocation kind.
274 auto MachORelocKind
= getRelocKind(RI
);
276 return MachORelocKind
.takeError();
278 Edge::Kind Kind
= Edge::Invalid
;
280 switch (*MachORelocKind
) {
282 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
283 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
285 return TargetSymbolOrErr
.takeError();
286 Addend
= *(const little32_t
*)FixupContent
;
287 Kind
= x86_64::BranchPCRel32
;
290 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
291 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
293 return TargetSymbolOrErr
.takeError();
294 Addend
= *(const little32_t
*)FixupContent
- 4;
295 Kind
= x86_64::Delta32
;
297 case MachOPCRel32GOTLoad
:
298 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
299 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
301 return TargetSymbolOrErr
.takeError();
302 Addend
= *(const little32_t
*)FixupContent
;
303 Kind
= x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable
;
305 return make_error
<JITLinkError
>("GOTLD at invalid offset " +
306 formatv("{0}", FixupOffset
));
308 case MachOPCRel32GOT
:
309 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
310 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
312 return TargetSymbolOrErr
.takeError();
313 Addend
= *(const little32_t
*)FixupContent
- 4;
314 Kind
= x86_64::RequestGOTAndTransformToDelta32
;
316 case MachOPCRel32TLV
:
317 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
318 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
320 return TargetSymbolOrErr
.takeError();
321 Addend
= *(const little32_t
*)FixupContent
;
322 Kind
= x86_64::RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable
;
324 return make_error
<JITLinkError
>("TLV at invalid offset " +
325 formatv("{0}", FixupOffset
));
328 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
329 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
331 return TargetSymbolOrErr
.takeError();
332 Addend
= *(const ulittle32_t
*)FixupContent
;
333 Kind
= x86_64::Pointer32
;
336 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
337 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
339 return TargetSymbolOrErr
.takeError();
340 Addend
= *(const ulittle64_t
*)FixupContent
;
341 Kind
= x86_64::Pointer64
;
343 case MachOPointer64Anon
: {
344 JITTargetAddress TargetAddress
= *(const ulittle64_t
*)FixupContent
;
345 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
346 TargetSymbol
= &*TargetSymbolOrErr
;
348 return TargetSymbolOrErr
.takeError();
349 Addend
= TargetAddress
- TargetSymbol
->getAddress();
350 Kind
= x86_64::Pointer64
;
353 case MachOPCRel32Minus1
:
354 case MachOPCRel32Minus2
:
355 case MachOPCRel32Minus4
:
356 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
357 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
359 return TargetSymbolOrErr
.takeError();
360 Addend
= *(const little32_t
*)FixupContent
- 4;
361 Kind
= x86_64::Delta32
;
363 case MachOPCRel32Anon
: {
364 JITTargetAddress TargetAddress
=
365 FixupAddress
+ 4 + *(const little32_t
*)FixupContent
;
366 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
367 TargetSymbol
= &*TargetSymbolOrErr
;
369 return TargetSymbolOrErr
.takeError();
370 Addend
= TargetAddress
- TargetSymbol
->getAddress() - 4;
371 Kind
= x86_64::Delta32
;
374 case MachOPCRel32Minus1Anon
:
375 case MachOPCRel32Minus2Anon
:
376 case MachOPCRel32Minus4Anon
: {
377 JITTargetAddress Delta
=
378 4 + static_cast<JITTargetAddress
>(
379 1ULL << (*MachORelocKind
- MachOPCRel32Minus1Anon
));
380 JITTargetAddress TargetAddress
=
381 FixupAddress
+ Delta
+ *(const little32_t
*)FixupContent
;
382 if (auto TargetSymbolOrErr
= findSymbolByAddress(TargetAddress
))
383 TargetSymbol
= &*TargetSymbolOrErr
;
385 return TargetSymbolOrErr
.takeError();
386 Addend
= TargetAddress
- TargetSymbol
->getAddress() - Delta
;
387 Kind
= x86_64::Delta32
;
390 case MachOSubtractor32
:
391 case MachOSubtractor64
: {
392 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
393 // parsePairRelocation handles the paired reloc, and returns the
394 // edge kind to be used (either Delta32/Delta64, or
395 // NegDelta32/NegDelta64, depending on the direction of the
396 // subtraction) along with the addend.
398 parsePairRelocation(*BlockToFix
, *MachORelocKind
, RI
,
399 FixupAddress
, FixupContent
, ++RelItr
, RelEnd
);
401 return PairInfo
.takeError();
402 std::tie(Kind
, TargetSymbol
, Addend
) = *PairInfo
;
403 assert(TargetSymbol
&& "No target symbol from parsePairRelocation?");
410 Edge
GE(Kind
, FixupAddress
- BlockToFix
->getAddress(), *TargetSymbol
,
412 printEdge(dbgs(), *BlockToFix
, GE
, x86_64::getEdgeKindName(Kind
));
415 BlockToFix
->addEdge(Kind
, FixupAddress
- BlockToFix
->getAddress(),
416 *TargetSymbol
, Addend
);
419 return Error::success();
423 class PerGraphGOTAndPLTStubsBuilder_MachO_x86_64
424 : public PerGraphGOTAndPLTStubsBuilder
<
425 PerGraphGOTAndPLTStubsBuilder_MachO_x86_64
> {
428 using PerGraphGOTAndPLTStubsBuilder
<
429 PerGraphGOTAndPLTStubsBuilder_MachO_x86_64
>::
430 PerGraphGOTAndPLTStubsBuilder
;
432 bool isGOTEdgeToFix(Edge
&E
) const {
433 return E
.getKind() == x86_64::RequestGOTAndTransformToDelta32
||
435 x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable
;
438 Symbol
&createGOTEntry(Symbol
&Target
) {
439 return x86_64::createAnonymousPointer(G
, getGOTSection(), &Target
);
442 void fixGOTEdge(Edge
&E
, Symbol
&GOTEntry
) {
443 // Fix the edge kind.
444 switch (E
.getKind()) {
445 case x86_64::RequestGOTAndTransformToDelta32
:
446 E
.setKind(x86_64::Delta32
);
448 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable
:
449 E
.setKind(x86_64::PCRel32GOTLoadREXRelaxable
);
452 llvm_unreachable("Not a GOT transform edge");
454 // Fix the target, leave the addend as-is.
455 E
.setTarget(GOTEntry
);
458 bool isExternalBranchEdge(Edge
&E
) {
459 return E
.getKind() == x86_64::BranchPCRel32
&& E
.getTarget().isExternal();
462 Symbol
&createPLTStub(Symbol
&Target
) {
463 return x86_64::createAnonymousPointerJumpStub(G
, getStubsSection(),
464 getGOTEntry(Target
));
467 void fixPLTEdge(Edge
&E
, Symbol
&Stub
) {
468 assert(E
.getKind() == x86_64::BranchPCRel32
&& "Not a Branch32 edge?");
469 assert(E
.getAddend() == 0 &&
470 "BranchPCRel32 edge has unexpected addend value");
472 // Set the edge kind to BranchPCRel32ToPtrJumpStubBypassable. We will use
473 // this to check for stub optimization opportunities in the
474 // optimizeMachO_x86_64_GOTAndStubs pass below.
475 E
.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable
);
480 Section
&getGOTSection() {
482 GOTSection
= &G
.createSection("$__GOT", sys::Memory::MF_READ
);
486 Section
&getStubsSection() {
488 auto StubsProt
= static_cast<sys::Memory::ProtectionFlags
>(
489 sys::Memory::MF_READ
| sys::Memory::MF_EXEC
);
490 StubsSection
= &G
.createSection("$__STUBS", StubsProt
);
492 return *StubsSection
;
495 Section
*GOTSection
= nullptr;
496 Section
*StubsSection
= nullptr;
504 class MachOJITLinker_x86_64
: public JITLinker
<MachOJITLinker_x86_64
> {
505 friend class JITLinker
<MachOJITLinker_x86_64
>;
508 MachOJITLinker_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
,
509 std::unique_ptr
<LinkGraph
> G
,
510 PassConfiguration PassConfig
)
511 : JITLinker(std::move(Ctx
), std::move(G
), std::move(PassConfig
)) {}
514 Error
applyFixup(LinkGraph
&G
, Block
&B
, const Edge
&E
) const {
515 return x86_64::applyFixup(G
, B
, E
, nullptr);
519 Expected
<std::unique_ptr
<LinkGraph
>>
520 createLinkGraphFromMachOObject_x86_64(MemoryBufferRef ObjectBuffer
) {
521 auto MachOObj
= object::ObjectFile::createMachOObjectFile(ObjectBuffer
);
523 return MachOObj
.takeError();
524 return MachOLinkGraphBuilder_x86_64(**MachOObj
).buildGraph();
527 void link_MachO_x86_64(std::unique_ptr
<LinkGraph
> G
,
528 std::unique_ptr
<JITLinkContext
> Ctx
) {
530 PassConfiguration Config
;
532 if (Ctx
->shouldAddDefaultTargetPasses(G
->getTargetTriple())) {
533 // Add eh-frame passses.
534 Config
.PrePrunePasses
.push_back(createEHFrameSplitterPass_MachO_x86_64());
535 Config
.PrePrunePasses
.push_back(createEHFrameEdgeFixerPass_MachO_x86_64());
537 // Add a mark-live pass.
538 if (auto MarkLive
= Ctx
->getMarkLivePass(G
->getTargetTriple()))
539 Config
.PrePrunePasses
.push_back(std::move(MarkLive
));
541 Config
.PrePrunePasses
.push_back(markAllSymbolsLive
);
543 // Add an in-place GOT/Stubs pass.
544 Config
.PostPrunePasses
.push_back(
545 PerGraphGOTAndPLTStubsBuilder_MachO_x86_64::asPass
);
547 // Add GOT/Stubs optimizer pass.
548 Config
.PreFixupPasses
.push_back(x86_64::optimize_x86_64_GOTAndStubs
);
551 if (auto Err
= Ctx
->modifyPassConfig(*G
, Config
))
552 return Ctx
->notifyFailed(std::move(Err
));
554 // Construct a JITLinker and run the link function.
555 MachOJITLinker_x86_64::link(std::move(Ctx
), std::move(G
), std::move(Config
));
558 LinkGraphPassFunction
createEHFrameSplitterPass_MachO_x86_64() {
559 return EHFrameSplitter("__TEXT,__eh_frame");
562 LinkGraphPassFunction
createEHFrameEdgeFixerPass_MachO_x86_64() {
563 return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize
,
564 x86_64::Delta64
, x86_64::Delta32
, x86_64::NegDelta32
);
567 } // end namespace jitlink
568 } // end namespace llvm