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:
43 if (!RI.r_pcrel && RI.r_length == 3)
44 return RI.r_extern ? Pointer64 : Pointer64Anon;
46 case MachO::X86_64_RELOC_SIGNED:
47 if (RI.r_pcrel && RI.r_length == 2)
48 return RI.r_extern ? PCRel32 : PCRel32Anon;
50 case MachO::X86_64_RELOC_BRANCH:
51 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
54 case MachO::X86_64_RELOC_GOT_LOAD:
55 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
56 return PCRel32GOTLoad;
58 case MachO::X86_64_RELOC_GOT:
59 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
62 case MachO::X86_64_RELOC_SUBTRACTOR:
63 // SUBTRACTOR must be non-pc-rel, extern, with length 2 or 3.
64 // Initially represent SUBTRACTOR relocations with 'Delta<W>'. They may
65 // be turned into NegDelta<W> by parsePairRelocation.
66 if (!RI.r_pcrel && RI.r_extern) {
69 else if (RI.r_length == 3)
73 case MachO::X86_64_RELOC_SIGNED_1:
74 if (RI.r_pcrel && RI.r_length == 2)
75 return RI.r_extern ? PCRel32Minus1 : PCRel32Minus1Anon;
77 case MachO::X86_64_RELOC_SIGNED_2:
78 if (RI.r_pcrel && RI.r_length == 2)
79 return RI.r_extern ? PCRel32Minus2 : PCRel32Minus2Anon;
81 case MachO::X86_64_RELOC_SIGNED_4:
82 if (RI.r_pcrel && RI.r_length == 2)
83 return RI.r_extern ? PCRel32Minus4 : PCRel32Minus4Anon;
85 case MachO::X86_64_RELOC_TLV:
86 if (RI.r_pcrel && RI.r_extern && RI.r_length == 2)
91 return make_error<JITLinkError>(
92 "Unsupported x86-64 relocation: address=" +
93 formatv("{0:x8}", RI.r_address) +
94 ", symbolnum=" + formatv("{0:x6}", RI.r_symbolnum) +
95 ", kind=" + formatv("{0:x1}", RI.r_type) +
96 ", pc_rel=" + (RI.r_pcrel ? "true" : "false") +
97 ", extern= " + (RI.r_extern ? "true" : "false") +
98 ", length=" + formatv("{0:d}", RI.r_length));
101 Expected<Atom &> findAtomBySymbolIndex(const MachO::relocation_info &RI) {
102 auto &Obj = getObject();
103 if (RI.r_symbolnum >= NumSymbols)
104 return make_error<JITLinkError>("Symbol index out of range");
105 auto SymI = Obj.getSymbolByIndex(RI.r_symbolnum);
106 auto Name = SymI->getName();
108 return Name.takeError();
109 return getGraph().getAtomByName(*Name);
112 MachO::relocation_info
113 getRelocationInfo(const object::relocation_iterator RelItr) {
114 MachO::any_relocation_info ARI =
115 getObject().getRelocation(RelItr->getRawDataRefImpl());
116 MachO::relocation_info RI;
117 memcpy(&RI, &ARI, sizeof(MachO::relocation_info));
121 using PairRelocInfo = std::tuple<MachOX86RelocationKind, Atom *, uint64_t>;
123 // Parses paired SUBTRACTOR/UNSIGNED relocations and, on success,
124 // returns the edge kind and addend to be used.
125 Expected<PairRelocInfo>
126 parsePairRelocation(DefinedAtom &AtomToFix, Edge::Kind SubtractorKind,
127 const MachO::relocation_info &SubRI,
128 JITTargetAddress FixupAddress, const char *FixupContent,
129 object::relocation_iterator &UnsignedRelItr,
130 object::relocation_iterator &RelEnd) {
131 using namespace support;
133 assert(((SubtractorKind == Delta32 && SubRI.r_length == 2) ||
134 (SubtractorKind == Delta64 && SubRI.r_length == 3)) &&
135 "Subtractor kind should match length");
136 assert(SubRI.r_extern && "SUBTRACTOR reloc symbol should be extern");
137 assert(!SubRI.r_pcrel && "SUBTRACTOR reloc should not be PCRel");
139 if (UnsignedRelItr == RelEnd)
140 return make_error<JITLinkError>("x86_64 SUBTRACTOR without paired "
141 "UNSIGNED relocation");
143 auto UnsignedRI = getRelocationInfo(UnsignedRelItr);
145 if (SubRI.r_address != UnsignedRI.r_address)
146 return make_error<JITLinkError>("x86_64 SUBTRACTOR and paired UNSIGNED "
147 "point to different addresses");
149 if (SubRI.r_length != UnsignedRI.r_length)
150 return make_error<JITLinkError>("length of x86_64 SUBTRACTOR and paired "
151 "UNSIGNED reloc must match");
153 auto FromAtom = findAtomBySymbolIndex(SubRI);
155 return FromAtom.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 'ToAtom' using symbol number or address, depending on whether the
165 // paired UNSIGNED relocation is extern.
166 Atom *ToAtom = nullptr;
167 if (UnsignedRI.r_extern) {
168 // Find target atom by symbol index.
169 if (auto ToAtomOrErr = findAtomBySymbolIndex(UnsignedRI))
170 ToAtom = &*ToAtomOrErr;
172 return ToAtomOrErr.takeError();
174 if (auto ToAtomOrErr = getGraph().findAtomByAddress(FixupValue))
175 ToAtom = &*ToAtomOrErr;
177 return ToAtomOrErr.takeError();
178 FixupValue -= ToAtom->getAddress();
181 MachOX86RelocationKind DeltaKind;
184 if (areLayoutLocked(AtomToFix, *FromAtom)) {
186 DeltaKind = (SubRI.r_length == 3) ? Delta64 : Delta32;
187 Addend = FixupValue + (FixupAddress - FromAtom->getAddress());
188 // FIXME: handle extern 'from'.
189 } else if (areLayoutLocked(AtomToFix, *ToAtom)) {
190 TargetAtom = &*FromAtom;
191 DeltaKind = (SubRI.r_length == 3) ? NegDelta64 : NegDelta32;
192 Addend = FixupValue - (FixupAddress - ToAtom->getAddress());
194 // AtomToFix was neither FromAtom nor ToAtom.
195 return make_error<JITLinkError>("SUBTRACTOR relocation must fix up "
196 "either 'A' or 'B' (or an atom in one "
197 "of their alt-entry groups)");
200 return PairRelocInfo(DeltaKind, TargetAtom, Addend);
203 Error addRelocations() override {
204 using namespace support;
205 auto &G = getGraph();
206 auto &Obj = getObject();
208 for (auto &S : Obj.sections()) {
210 JITTargetAddress SectionAddress = S.getAddress();
212 for (auto RelItr = S.relocation_begin(), RelEnd = S.relocation_end();
213 RelItr != RelEnd; ++RelItr) {
215 MachO::relocation_info RI = getRelocationInfo(RelItr);
217 // Sanity check the relocation kind.
218 auto Kind = getRelocationKind(RI);
220 return Kind.takeError();
222 // Find the address of the value to fix up.
223 JITTargetAddress FixupAddress = SectionAddress + (uint32_t)RI.r_address;
226 dbgs() << "Processing relocation at "
227 << format("0x%016" PRIx64, FixupAddress) << "\n";
230 // Find the atom that the fixup points to.
231 DefinedAtom *AtomToFix = nullptr;
233 auto AtomToFixOrErr = G.findAtomByAddress(FixupAddress);
235 return AtomToFixOrErr.takeError();
236 AtomToFix = &*AtomToFixOrErr;
239 if (FixupAddress + static_cast<JITTargetAddress>(1ULL << RI.r_length) >
240 AtomToFix->getAddress() + AtomToFix->getContent().size())
241 return make_error<JITLinkError>(
242 "Relocation content extends past end of fixup atom");
244 // Get a pointer to the fixup content.
245 const char *FixupContent = AtomToFix->getContent().data() +
246 (FixupAddress - AtomToFix->getAddress());
248 // The target atom and addend will be populated by the switch below.
249 Atom *TargetAtom = nullptr;
257 if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI))
258 TargetAtom = &*TargetAtomOrErr;
260 return TargetAtomOrErr.takeError();
261 Addend = *(const ulittle32_t *)FixupContent;
264 if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI))
265 TargetAtom = &*TargetAtomOrErr;
267 return TargetAtomOrErr.takeError();
268 Addend = *(const ulittle64_t *)FixupContent;
270 case Pointer64Anon: {
271 JITTargetAddress TargetAddress = *(const ulittle64_t *)FixupContent;
272 if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress))
273 TargetAtom = &*TargetAtomOrErr;
275 return TargetAtomOrErr.takeError();
276 Addend = TargetAddress - TargetAtom->getAddress();
282 if (auto TargetAtomOrErr = findAtomBySymbolIndex(RI))
283 TargetAtom = &*TargetAtomOrErr;
285 return TargetAtomOrErr.takeError();
286 Addend = *(const ulittle32_t *)FixupContent +
287 (1 << (*Kind - PCRel32Minus1));
290 JITTargetAddress TargetAddress =
291 FixupAddress + 4 + *(const ulittle32_t *)FixupContent;
292 if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress))
293 TargetAtom = &*TargetAtomOrErr;
295 return TargetAtomOrErr.takeError();
296 Addend = TargetAddress - TargetAtom->getAddress();
299 case PCRel32Minus1Anon:
300 case PCRel32Minus2Anon:
301 case PCRel32Minus4Anon: {
302 JITTargetAddress Delta =
303 static_cast<JITTargetAddress>(1ULL << (*Kind - PCRel32Minus1Anon));
304 JITTargetAddress TargetAddress =
305 FixupAddress + 4 + Delta + *(const ulittle32_t *)FixupContent;
306 if (auto TargetAtomOrErr = G.findAtomByAddress(TargetAddress))
307 TargetAtom = &*TargetAtomOrErr;
309 return TargetAtomOrErr.takeError();
310 Addend = TargetAddress - TargetAtom->getAddress();
315 // We use Delta32/Delta64 to represent SUBTRACTOR relocations.
316 // parsePairRelocation handles the paired reloc, and returns the
317 // edge kind to be used (either Delta32/Delta64, or
318 // NegDelta32/NegDelta64, depending on the direction of the
319 // subtraction) along with the addend.
321 parsePairRelocation(*AtomToFix, *Kind, RI, FixupAddress,
322 FixupContent, ++RelItr, RelEnd);
324 return PairInfo.takeError();
325 std::tie(*Kind, TargetAtom, Addend) = *PairInfo;
326 assert(TargetAtom && "No target atom from parsePairRelocation?");
330 llvm_unreachable("Special relocation kind should not appear in "
335 Edge GE(*Kind, FixupAddress - AtomToFix->getAddress(), *TargetAtom,
337 printEdge(dbgs(), *AtomToFix, GE,
338 getMachOX86RelocationKindName(*Kind));
341 AtomToFix->addEdge(*Kind, FixupAddress - AtomToFix->getAddress(),
342 *TargetAtom, Addend);
345 return Error::success();
348 unsigned NumSymbols = 0;
351 class MachO_x86_64_GOTAndStubsBuilder
352 : public BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder> {
354 MachO_x86_64_GOTAndStubsBuilder(AtomGraph &G)
355 : BasicGOTAndStubsBuilder<MachO_x86_64_GOTAndStubsBuilder>(G) {}
357 bool isGOTEdge(Edge &E) const {
358 return E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad;
361 DefinedAtom &createGOTEntry(Atom &Target) {
362 auto &GOTEntryAtom = G.addAnonymousAtom(getGOTSection(), 0x0, 8);
363 GOTEntryAtom.setContent(
364 StringRef(reinterpret_cast<const char *>(NullGOTEntryContent), 8));
365 GOTEntryAtom.addEdge(Pointer64, 0, Target, 0);
369 void fixGOTEdge(Edge &E, Atom &GOTEntry) {
370 assert((E.getKind() == PCRel32GOT || E.getKind() == PCRel32GOTLoad) &&
373 E.setTarget(GOTEntry);
374 // Leave the edge addend as-is.
377 bool isExternalBranchEdge(Edge &E) {
378 return E.getKind() == Branch32 && !E.getTarget().isDefined();
381 DefinedAtom &createStub(Atom &Target) {
382 auto &StubAtom = G.addAnonymousAtom(getStubsSection(), 0x0, 2);
384 StringRef(reinterpret_cast<const char *>(StubContent), 6));
386 // Re-use GOT entries for stub targets.
387 auto &GOTEntryAtom = getGOTEntryAtom(Target);
388 StubAtom.addEdge(PCRel32, 2, GOTEntryAtom, 0);
393 void fixExternalBranchEdge(Edge &E, Atom &Stub) {
394 assert(E.getKind() == Branch32 && "Not a Branch32 edge?");
395 assert(E.getAddend() == 0 && "Branch32 edge has non-zero addend?");
400 Section &getGOTSection() {
402 GOTSection = &G.createSection("$__GOT", 8, sys::Memory::MF_READ, false);
406 Section &getStubsSection() {
408 auto StubsProt = static_cast<sys::Memory::ProtectionFlags>(
409 sys::Memory::MF_READ | sys::Memory::MF_EXEC);
410 StubsSection = &G.createSection("$__STUBS", 8, StubsProt, false);
412 return *StubsSection;
415 static const uint8_t NullGOTEntryContent[8];
416 static const uint8_t StubContent[6];
417 Section *GOTSection = nullptr;
418 Section *StubsSection = nullptr;
421 const uint8_t MachO_x86_64_GOTAndStubsBuilder::NullGOTEntryContent[8] = {
422 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
423 const uint8_t MachO_x86_64_GOTAndStubsBuilder::StubContent[6] = {
424 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
430 class MachOJITLinker_x86_64 : public JITLinker<MachOJITLinker_x86_64> {
431 friend class JITLinker<MachOJITLinker_x86_64>;
434 MachOJITLinker_x86_64(std::unique_ptr<JITLinkContext> Ctx,
435 PassConfiguration PassConfig)
436 : JITLinker(std::move(Ctx), std::move(PassConfig)) {}
439 StringRef getEdgeKindName(Edge::Kind R) const override {
440 return getMachOX86RelocationKindName(R);
443 Expected<std::unique_ptr<AtomGraph>>
444 buildGraph(MemoryBufferRef ObjBuffer) override {
445 auto MachOObj = object::ObjectFile::createMachOObjectFile(ObjBuffer);
447 return MachOObj.takeError();
448 return MachOAtomGraphBuilder_x86_64(**MachOObj).buildGraph();
451 static Error targetOutOfRangeError(const Atom &A, const Edge &E) {
454 raw_string_ostream ErrStream(ErrMsg);
455 ErrStream << "Relocation target out of range: ";
456 printEdge(ErrStream, A, E, getMachOX86RelocationKindName(E.getKind()));
459 return make_error<JITLinkError>(std::move(ErrMsg));
462 Error applyFixup(DefinedAtom &A, const Edge &E, char *AtomWorkingMem) const {
463 using namespace support;
465 char *FixupPtr = AtomWorkingMem + E.getOffset();
466 JITTargetAddress FixupAddress = A.getAddress() + E.getOffset();
468 switch (E.getKind()) {
473 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
474 if (Value < std::numeric_limits<int32_t>::min() ||
475 Value > std::numeric_limits<int32_t>::max())
476 return targetOutOfRangeError(A, E);
477 *(little32_t *)FixupPtr = Value;
481 case Pointer64Anon: {
482 uint64_t Value = E.getTarget().getAddress() + E.getAddend();
483 *(ulittle64_t *)FixupPtr = Value;
488 case PCRel32Minus4: {
489 int Delta = 4 + (1 << (E.getKind() - PCRel32Minus1));
491 E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
492 if (Value < std::numeric_limits<int32_t>::min() ||
493 Value > std::numeric_limits<int32_t>::max())
494 return targetOutOfRangeError(A, E);
495 *(little32_t *)FixupPtr = Value;
498 case PCRel32Minus1Anon:
499 case PCRel32Minus2Anon:
500 case PCRel32Minus4Anon: {
501 int Delta = 4 + (1 << (E.getKind() - PCRel32Minus1Anon));
503 E.getTarget().getAddress() - (FixupAddress + Delta) + E.getAddend();
504 if (Value < std::numeric_limits<int32_t>::min() ||
505 Value > std::numeric_limits<int32_t>::max())
506 return targetOutOfRangeError(A, E);
507 *(little32_t *)FixupPtr = Value;
515 if (E.getKind() == Delta32 || E.getKind() == Delta64)
516 Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
518 Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
520 if (E.getKind() == Delta32 || E.getKind() == NegDelta32) {
521 if (Value < std::numeric_limits<int32_t>::min() ||
522 Value > std::numeric_limits<int32_t>::max())
523 return targetOutOfRangeError(A, E);
524 *(little32_t *)FixupPtr = Value;
526 *(little64_t *)FixupPtr = Value;
530 llvm_unreachable("Unrecognized edge kind");
533 return Error::success();
536 uint64_t NullValue = 0;
539 void jitLink_MachO_x86_64(std::unique_ptr<JITLinkContext> Ctx) {
540 PassConfiguration Config;
541 Triple TT("x86_64-apple-macosx");
543 if (Ctx->shouldAddDefaultTargetPasses(TT)) {
544 // Add a mark-live pass.
545 if (auto MarkLive = Ctx->getMarkLivePass(TT))
546 Config.PrePrunePasses.push_back(std::move(MarkLive));
548 Config.PrePrunePasses.push_back(markAllAtomsLive);
550 // Add an in-place GOT/Stubs pass.
551 Config.PostPrunePasses.push_back([](AtomGraph &G) -> Error {
552 MachO_x86_64_GOTAndStubsBuilder(G).run();
553 return Error::success();
557 if (auto Err = Ctx->modifyPassConfig(TT, Config))
558 return Ctx->notifyFailed(std::move(Err));
560 // Construct a JITLinker and run the link function.
561 MachOJITLinker_x86_64::link(std::move(Ctx), std::move(Config));
564 StringRef getMachOX86RelocationKindName(Edge::Kind R) {
571 return "Pointer64Anon";
575 return "PCRel32Minus1";
577 return "PCRel32Minus2";
579 return "PCRel32Minus4";
581 return "PCRel32Anon";
582 case PCRel32Minus1Anon:
583 return "PCRel32Minus1Anon";
584 case PCRel32Minus2Anon:
585 return "PCRel32Minus2Anon";
586 case PCRel32Minus4Anon:
587 return "PCRel32Minus4Anon";
589 return "PCRel32GOTLoad";
603 return getGenericEdgeKindName(static_cast<Edge::Kind>(R));
607 } // end namespace jitlink
608 } // end namespace llvm