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
, orc::ExecutorAddr 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
, ToSymbolSec
->Address
);
174 assert(ToSymbol
&& "No symbol for section");
175 FixupValue
-= ToSymbol
->getAddress().getValue();
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 orc::ExecutorAddr
SectionAddress(S
.getAddress());
211 // Skip relocations virtual sections.
213 if (S
.relocation_begin() != S
.relocation_end())
214 return make_error
<JITLinkError
>("Virtual section contains "
220 findSectionByIndex(Obj
.getSectionIndex(S
.getRawDataRefImpl()));
222 return NSec
.takeError();
224 // Skip relocations for MachO sections without corresponding graph
227 if (!NSec
->GraphSection
) {
229 dbgs() << " Skipping relocations for MachO section "
230 << NSec
->SegName
<< "/" << NSec
->SectName
231 << " which has no associated graph section\n";
237 // Add relocations for section.
238 for (auto RelItr
= S
.relocation_begin(), RelEnd
= S
.relocation_end();
239 RelItr
!= RelEnd
; ++RelItr
) {
241 MachO::relocation_info RI
= getRelocationInfo(RelItr
);
243 // Find the address of the value to fix up.
244 auto FixupAddress
= SectionAddress
+ (uint32_t)RI
.r_address
;
247 dbgs() << " " << NSec
->SectName
<< " + "
248 << formatv("{0:x8}", RI
.r_address
) << ":\n";
251 // Find the block that the fixup points to.
252 Block
*BlockToFix
= nullptr;
254 auto SymbolToFixOrErr
= findSymbolByAddress(*NSec
, FixupAddress
);
255 if (!SymbolToFixOrErr
)
256 return SymbolToFixOrErr
.takeError();
257 BlockToFix
= &SymbolToFixOrErr
->getBlock();
260 if (FixupAddress
+ orc::ExecutorAddrDiff(1ULL << RI
.r_length
) >
261 BlockToFix
->getAddress() + BlockToFix
->getContent().size())
262 return make_error
<JITLinkError
>(
263 "Relocation extends past end of fixup block");
265 // Get a pointer to the fixup content.
266 const char *FixupContent
= BlockToFix
->getContent().data() +
267 (FixupAddress
- BlockToFix
->getAddress());
269 size_t FixupOffset
= FixupAddress
- BlockToFix
->getAddress();
271 // The target symbol and addend will be populated by the switch below.
272 Symbol
*TargetSymbol
= nullptr;
275 // Validate the relocation kind.
276 auto MachORelocKind
= getRelocKind(RI
);
278 return MachORelocKind
.takeError();
280 Edge::Kind Kind
= Edge::Invalid
;
282 switch (*MachORelocKind
) {
284 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
285 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
287 return TargetSymbolOrErr
.takeError();
288 Addend
= *(const little32_t
*)FixupContent
;
289 Kind
= x86_64::BranchPCRel32
;
292 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
293 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
295 return TargetSymbolOrErr
.takeError();
296 Addend
= *(const little32_t
*)FixupContent
- 4;
297 Kind
= x86_64::Delta32
;
299 case MachOPCRel32GOTLoad
:
300 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
301 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
303 return TargetSymbolOrErr
.takeError();
304 Addend
= *(const little32_t
*)FixupContent
;
305 Kind
= x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable
;
307 return make_error
<JITLinkError
>("GOTLD at invalid offset " +
308 formatv("{0}", FixupOffset
));
310 case MachOPCRel32GOT
:
311 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
312 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
314 return TargetSymbolOrErr
.takeError();
315 Addend
= *(const little32_t
*)FixupContent
- 4;
316 Kind
= x86_64::RequestGOTAndTransformToDelta32
;
318 case MachOPCRel32TLV
:
319 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
320 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
322 return TargetSymbolOrErr
.takeError();
323 Addend
= *(const little32_t
*)FixupContent
;
324 Kind
= x86_64::RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable
;
326 return make_error
<JITLinkError
>("TLV at invalid offset " +
327 formatv("{0}", FixupOffset
));
330 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
331 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
333 return TargetSymbolOrErr
.takeError();
334 Addend
= *(const ulittle32_t
*)FixupContent
;
335 Kind
= x86_64::Pointer32
;
338 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
339 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
341 return TargetSymbolOrErr
.takeError();
342 Addend
= *(const ulittle64_t
*)FixupContent
;
343 Kind
= x86_64::Pointer64
;
345 case MachOPointer64Anon
: {
346 orc::ExecutorAddr
TargetAddress(*(const ulittle64_t
*)FixupContent
);
347 auto TargetNSec
= findSectionByIndex(RI
.r_symbolnum
- 1);
349 return TargetNSec
.takeError();
350 if (auto TargetSymbolOrErr
=
351 findSymbolByAddress(*TargetNSec
, TargetAddress
))
352 TargetSymbol
= &*TargetSymbolOrErr
;
354 return TargetSymbolOrErr
.takeError();
355 Addend
= TargetAddress
- TargetSymbol
->getAddress();
356 Kind
= x86_64::Pointer64
;
359 case MachOPCRel32Minus1
:
360 case MachOPCRel32Minus2
:
361 case MachOPCRel32Minus4
:
362 if (auto TargetSymbolOrErr
= findSymbolByIndex(RI
.r_symbolnum
))
363 TargetSymbol
= TargetSymbolOrErr
->GraphSymbol
;
365 return TargetSymbolOrErr
.takeError();
366 Addend
= *(const little32_t
*)FixupContent
- 4;
367 Kind
= x86_64::Delta32
;
369 case MachOPCRel32Anon
: {
370 orc::ExecutorAddr
TargetAddress(FixupAddress
+ 4 +
371 *(const little32_t
*)FixupContent
);
372 auto TargetNSec
= findSectionByIndex(RI
.r_symbolnum
- 1);
374 return TargetNSec
.takeError();
375 if (auto TargetSymbolOrErr
=
376 findSymbolByAddress(*TargetNSec
, TargetAddress
))
377 TargetSymbol
= &*TargetSymbolOrErr
;
379 return TargetSymbolOrErr
.takeError();
380 Addend
= TargetAddress
- TargetSymbol
->getAddress() - 4;
381 Kind
= x86_64::Delta32
;
384 case MachOPCRel32Minus1Anon
:
385 case MachOPCRel32Minus2Anon
:
386 case MachOPCRel32Minus4Anon
: {
387 orc::ExecutorAddrDiff Delta
=
388 4 + orc::ExecutorAddrDiff(
389 1ULL << (*MachORelocKind
- MachOPCRel32Minus1Anon
));
390 orc::ExecutorAddr TargetAddress
=
391 FixupAddress
+ Delta
+ *(const little32_t
*)FixupContent
;
392 auto TargetNSec
= findSectionByIndex(RI
.r_symbolnum
- 1);
394 return TargetNSec
.takeError();
395 if (auto TargetSymbolOrErr
=
396 findSymbolByAddress(*TargetNSec
, TargetAddress
))
397 TargetSymbol
= &*TargetSymbolOrErr
;
399 return TargetSymbolOrErr
.takeError();
400 Addend
= TargetAddress
- TargetSymbol
->getAddress() - Delta
;
401 Kind
= x86_64::Delta32
;
404 case MachOSubtractor32
:
405 case MachOSubtractor64
: {
406 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
407 // parsePairRelocation handles the paired reloc, and returns the
408 // edge kind to be used (either Delta32/Delta64, or
409 // NegDelta32/NegDelta64, depending on the direction of the
410 // subtraction) along with the addend.
412 parsePairRelocation(*BlockToFix
, *MachORelocKind
, RI
,
413 FixupAddress
, FixupContent
, ++RelItr
, RelEnd
);
415 return PairInfo
.takeError();
416 std::tie(Kind
, TargetSymbol
, Addend
) = *PairInfo
;
417 assert(TargetSymbol
&& "No target symbol from parsePairRelocation?");
424 Edge
GE(Kind
, FixupAddress
- BlockToFix
->getAddress(), *TargetSymbol
,
426 printEdge(dbgs(), *BlockToFix
, GE
, x86_64::getEdgeKindName(Kind
));
429 BlockToFix
->addEdge(Kind
, FixupAddress
- BlockToFix
->getAddress(),
430 *TargetSymbol
, Addend
);
433 return Error::success();
437 Error
buildGOTAndStubs_MachO_x86_64(LinkGraph
&G
) {
438 x86_64::GOTTableManager GOT
;
439 x86_64::PLTTableManager
PLT(GOT
);
440 visitExistingEdges(G
, GOT
, PLT
);
441 return Error::success();
449 class MachOJITLinker_x86_64
: public JITLinker
<MachOJITLinker_x86_64
> {
450 friend class JITLinker
<MachOJITLinker_x86_64
>;
453 MachOJITLinker_x86_64(std::unique_ptr
<JITLinkContext
> Ctx
,
454 std::unique_ptr
<LinkGraph
> G
,
455 PassConfiguration PassConfig
)
456 : JITLinker(std::move(Ctx
), std::move(G
), std::move(PassConfig
)) {}
459 Error
applyFixup(LinkGraph
&G
, Block
&B
, const Edge
&E
) const {
460 return x86_64::applyFixup(G
, B
, E
, nullptr);
464 Expected
<std::unique_ptr
<LinkGraph
>>
465 createLinkGraphFromMachOObject_x86_64(MemoryBufferRef ObjectBuffer
) {
466 auto MachOObj
= object::ObjectFile::createMachOObjectFile(ObjectBuffer
);
468 return MachOObj
.takeError();
469 return MachOLinkGraphBuilder_x86_64(**MachOObj
).buildGraph();
472 void link_MachO_x86_64(std::unique_ptr
<LinkGraph
> G
,
473 std::unique_ptr
<JITLinkContext
> Ctx
) {
475 PassConfiguration Config
;
477 if (Ctx
->shouldAddDefaultTargetPasses(G
->getTargetTriple())) {
478 // Add eh-frame passses.
479 Config
.PrePrunePasses
.push_back(createEHFrameSplitterPass_MachO_x86_64());
480 Config
.PrePrunePasses
.push_back(createEHFrameEdgeFixerPass_MachO_x86_64());
482 // Add compact unwind splitter pass.
483 Config
.PrePrunePasses
.push_back(
484 CompactUnwindSplitter("__LD,__compact_unwind"));
486 // Add a mark-live pass.
487 if (auto MarkLive
= Ctx
->getMarkLivePass(G
->getTargetTriple()))
488 Config
.PrePrunePasses
.push_back(std::move(MarkLive
));
490 Config
.PrePrunePasses
.push_back(markAllSymbolsLive
);
492 // Add an in-place GOT/Stubs pass.
493 Config
.PostPrunePasses
.push_back(buildGOTAndStubs_MachO_x86_64
);
495 // Add GOT/Stubs optimizer pass.
496 Config
.PreFixupPasses
.push_back(x86_64::optimizeGOTAndStubAccesses
);
499 if (auto Err
= Ctx
->modifyPassConfig(*G
, Config
))
500 return Ctx
->notifyFailed(std::move(Err
));
502 // Construct a JITLinker and run the link function.
503 MachOJITLinker_x86_64::link(std::move(Ctx
), std::move(G
), std::move(Config
));
506 LinkGraphPassFunction
createEHFrameSplitterPass_MachO_x86_64() {
507 return EHFrameSplitter("__TEXT,__eh_frame");
510 LinkGraphPassFunction
createEHFrameEdgeFixerPass_MachO_x86_64() {
511 return EHFrameEdgeFixer("__TEXT,__eh_frame", x86_64::PointerSize
,
512 x86_64::Delta64
, x86_64::Delta32
, x86_64::NegDelta32
);
515 } // end namespace jitlink
516 } // end namespace llvm