1//===-- x86_64.h - Generic JITLink x86-64 edge kinds, utilities -*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8//
9// Generic utilities for graphs representing x86-64 objects.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_JITLINK_X86_64_H
14#define LLVM_EXECUTIONENGINE_JITLINK_X86_64_H
15
16#include "llvm/ExecutionEngine/JITLink/JITLink.h"
17#include "llvm/ExecutionEngine/JITLink/TableManager.h"
18
19#include <limits>
20
21namespace llvm {
22namespace jitlink {
23namespace x86_64 {
24
25/// Represents x86-64 fixups and other x86-64-specific edge kinds.
26enum EdgeKind_x86_64 : Edge::Kind {
27
28 /// A plain 64-bit pointer value relocation.
29 ///
30 /// Fixup expression:
31 /// Fixup <- Target + Addend : uint64
32 ///
33 Pointer64 = Edge::FirstRelocation,
34
35 /// A plain 32-bit pointer value relocation.
36 ///
37 /// Fixup expression:
38 /// Fixup <- Target + Addend : uint32
39 ///
40 /// Errors:
41 /// - The target must reside in the low 32-bits of the address space,
42 /// otherwise an out-of-range error will be returned.
43 ///
44 Pointer32,
45
46 /// A signed 32-bit pointer value relocation
47 ///
48 /// Fixup expression:
49 /// Fixup <- Target + Addend : int32
50 ///
51 /// Errors:
52 /// - The target must reside in the signed 32-bits([-2**31, 2**32 - 1]) of
53 /// the address space, otherwise an out-of-range error will be returned.
54 Pointer32Signed,
55
56 /// A plain 16-bit pointer value relocation.
57 ///
58 /// Fixup expression:
59 /// Fixup <- Target + Addend : uint16
60 ///
61 /// Errors:
62 /// - The target must reside in the low 16-bits of the address space,
63 /// otherwise an out-of-range error will be returned.
64 ///
65 Pointer16,
66
67 /// A 64-bit delta.
68 ///
69 /// Delta from the fixup to the target.
70 ///
71 /// Fixup expression:
72 /// Fixup <- Target - Fixup + Addend : int64
73 ///
74 Delta64,
75
76 /// A 32-bit delta.
77 ///
78 /// Delta from the fixup to the target.
79 ///
80 /// Fixup expression:
81 /// Fixup <- Target - Fixup + Addend : int64
82 ///
83 /// Errors:
84 /// - The result of the fixup expression must fit into an int32, otherwise
85 /// an out-of-range error will be returned.
86 ///
87 Delta32,
88
89 /// A 64-bit negative delta.
90 ///
91 /// Delta from target back to the fixup.
92 ///
93 /// Fixup expression:
94 /// Fixup <- Fixup - Target + Addend : int64
95 ///
96 NegDelta64,
97
98 /// A 32-bit negative delta.
99 ///
100 /// Delta from the target back to the fixup.
101 ///
102 /// Fixup expression:
103 /// Fixup <- Fixup - Target + Addend : int32
104 ///
105 /// Errors:
106 /// - The result of the fixup expression must fit into an int32, otherwise
107 /// an out-of-range error will be returned.
108 NegDelta32,
109
110 /// A 64-bit GOT delta.
111 ///
112 /// Delta from the global offset table to the target
113 ///
114 /// Fixup expression:
115 /// Fixup <- Target - GOTSymbol + Addend : int64
116 ///
117 /// Errors:
118 /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
119 /// symbol was not been defined.
120 Delta64FromGOT,
121
122 /// A 32-bit PC-relative branch.
123 ///
124 /// Represents a PC-relative call or branch to a target. This can be used to
125 /// identify, record, and/or patch call sites.
126 ///
127 /// The fixup expression for this kind includes an implicit offset to account
128 /// for the PC (unlike the Delta edges) so that a Branch32PCRel with a target
129 /// T and addend zero is a call/branch to the start (offset zero) of T.
130 ///
131 /// Fixup expression:
132 /// Fixup <- Target - (Fixup + 4) + Addend : int32
133 ///
134 /// Errors:
135 /// - The result of the fixup expression must fit into an int32, otherwise
136 /// an out-of-range error will be returned.
137 ///
138 BranchPCRel32,
139
140 /// A 32-bit PC-relative relocation.
141 ///
142 /// Represents a data/control flow instruction using PC-relative addressing
143 /// to a target.
144 ///
145 /// The fixup expression for this kind includes an implicit offset to account
146 /// for the PC (unlike the Delta edges) so that a PCRel32 with a target
147 /// T and addend zero is a call/branch to the start (offset zero) of T.
148 ///
149 /// Fixup expression:
150 /// Fixup <- Target - (Fixup + 4) + Addend : int32
151 ///
152 /// Errors:
153 /// - The result of the fixup expression must fit into an int32, otherwise
154 /// an out-of-range error will be returned.
155 ///
156 PCRel32,
157
158 /// A 32-bit PC-relative branch to a pointer jump stub.
159 ///
160 /// The target of this relocation should be a pointer jump stub of the form:
161 ///
162 /// \code{.s}
163 /// .text
164 /// jmpq *tgtptr(%rip)
165 /// ; ...
166 ///
167 /// .data
168 /// tgtptr:
169 /// .quad 0
170 /// \endcode
171 ///
172 /// This edge kind has the same fixup expression as BranchPCRel32, but further
173 /// identifies the call/branch as being to a pointer jump stub. For edges of
174 /// this kind the jump stub should not be bypassed (use
175 /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location
176 /// target may be recorded to allow manipulation at runtime.
177 ///
178 /// Fixup expression:
179 /// Fixup <- Target - Fixup + Addend - 4 : int32
180 ///
181 /// Errors:
182 /// - The result of the fixup expression must fit into an int32, otherwise
183 /// an out-of-range error will be returned.
184 ///
185 BranchPCRel32ToPtrJumpStub,
186
187 /// A relaxable version of BranchPCRel32ToPtrJumpStub.
188 ///
189 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub,
190 /// but identifies the call/branch as being to a pointer jump stub that may be
191 /// bypassed with a direct jump to the ultimate target if the ultimate target
192 /// is within range of the fixup location.
193 ///
194 /// Fixup expression:
195 /// Fixup <- Target - Fixup + Addend - 4: int32
196 ///
197 /// Errors:
198 /// - The result of the fixup expression must fit into an int32, otherwise
199 /// an out-of-range error will be returned.
200 ///
201 BranchPCRel32ToPtrJumpStubBypassable,
202
203 /// A GOT entry getter/constructor, transformed to Delta32 pointing at the GOT
204 /// entry for the original target.
205 ///
206 /// Indicates that this edge should be transformed into a Delta32 targeting
207 /// the GOT entry for the edge's current target, maintaining the same addend.
208 /// A GOT entry for the target should be created if one does not already
209 /// exist.
210 ///
211 /// Edges of this kind are usually handled by a GOT builder pass inserted by
212 /// default.
213 ///
214 /// Fixup expression:
215 /// NONE
216 ///
217 /// Errors:
218 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
219 /// phase will result in an assert/unreachable during the fixup phase.
220 ///
221 RequestGOTAndTransformToDelta32,
222
223 /// A GOT entry getter/constructor, transformed to Delta64 pointing at the GOT
224 /// entry for the original target.
225 ///
226 /// Indicates that this edge should be transformed into a Delta64 targeting
227 /// the GOT entry for the edge's current target, maintaining the same addend.
228 /// A GOT entry for the target should be created if one does not already
229 /// exist.
230 ///
231 /// Edges of this kind are usually handled by a GOT builder pass inserted by
232 /// default.
233 ///
234 /// Fixup expression:
235 /// NONE
236 ///
237 /// Errors:
238 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
239 /// phase will result in an assert/unreachable during the fixup phase.
240 ///
241 RequestGOTAndTransformToDelta64,
242
243 /// A GOT entry offset within GOT getter/constructor, transformed to
244 /// Delta64FromGOT
245 /// pointing at the GOT entry for the original target
246 ///
247 /// Indicates that this edge should be transformed into a Delta64FromGOT
248 /// targeting
249 /// the GOT entry for the edge's current target, maintaining the same addend.
250 /// A GOT entry for the target should be created if one does not already
251 /// exist.
252 ///
253 /// Edges of this kind are usually handled by a GOT builder pass inserted by
254 /// default
255 ///
256 /// Fixup expression:
257 /// NONE
258 ///
259 /// Errors:
260 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
261 /// phase will result in an assert/unreachable during the fixup phase
262 RequestGOTAndTransformToDelta64FromGOT,
263
264 /// A PC-relative load of a GOT entry, relaxable if GOT entry target is
265 /// in-range of the fixup
266 ///
267 /// TODO: Explain the optimization
268 ///
269 /// Fixup expression
270 /// Fixup <- Target - (Fixup + 4) + Addend : int32
271 ///
272 /// Errors:
273 /// - The result of the fixup expression must fit into an int32, otherwise
274 /// an out-of-range error will be returned.
275 //
276 PCRel32GOTLoadRelaxable,
277
278 /// A PC-relative REX load of a GOT entry, relaxable if GOT entry target
279 /// is in-range of the fixup.
280 ///
281 /// If the GOT entry target is in-range of the fixup then the load from the
282 /// GOT may be replaced with a direct memory address calculation.
283 ///
284 /// Fixup expression:
285 /// Fixup <- Target - (Fixup + 4) + Addend : int32
286 ///
287 /// Errors:
288 /// - The result of the fixup expression must fit into an int32, otherwise
289 /// an out-of-range error will be returned.
290 ///
291 PCRel32GOTLoadREXRelaxable,
292
293 /// A GOT entry getter/constructor, transformed to
294 /// PCRel32ToGOTLoadREXRelaxable pointing at the GOT entry for the original
295 /// target.
296 ///
297 /// Indicates that this edge should be lowered to a PC32ToGOTLoadREXRelaxable
298 /// targeting the GOT entry for the edge's current target, maintaining the
299 /// same addend. A GOT entry for the target should be created if one does not
300 /// already exist.
301 ///
302 /// Edges of this kind are usually lowered by a GOT builder pass inserted by
303 /// default.
304 ///
305 /// Fixup expression:
306 /// NONE
307 ///
308 /// Errors:
309 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
310 /// phase will result in an assert/unreachable during the fixup phase.
311 ///
312 RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable,
313
314 /// A GOT entry getter/constructor, transformed to
315 /// PCRel32ToGOTLoadRelaxable pointing at the GOT entry for the original
316 /// target.
317 ///
318 /// Indicates that this edge should be lowered to a PC32ToGOTLoadRelaxable
319 /// targeting the GOT entry for the edge's current target, maintaining the
320 /// same addend. A GOT entry for the target should be created if one does not
321 /// already exist.
322 ///
323 /// Edges of this kind are usually lowered by a GOT builder pass inserted by
324 /// default.
325 ///
326 /// Fixup expression:
327 /// NONE
328 ///
329 /// Errors:
330 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
331 /// phase will result in an assert/unreachable during the fixup phase.
332 ///
333 RequestGOTAndTransformToPCRel32GOTLoadRelaxable,
334
335 /// A PC-relative REX load of a Thread Local Variable Pointer (TLVP) entry,
336 /// relaxable if the TLVP entry target is in-range of the fixup.
337 ///
338 /// If the TLVP entry target is in-range of the fixup then the load from the
339 /// TLVP may be replaced with a direct memory address calculation.
340 ///
341 /// The target of this edge must be a thread local variable entry of the form
342 /// .quad <tlv getter thunk>
343 /// .quad <tlv key>
344 /// .quad <tlv initializer>
345 ///
346 /// Fixup expression:
347 /// Fixup <- Target - (Fixup + 4) + Addend : int32
348 ///
349 /// Errors:
350 /// - The result of the fixup expression must fit into an int32, otherwise
351 /// an out-of-range error will be returned.
352 /// - The target must be either external, or a TLV entry of the required
353 /// form, otherwise a malformed TLV entry error will be returned.
354 ///
355 PCRel32TLVPLoadREXRelaxable,
356
357 /// TODO: Explain the generic edge kind
358 RequestTLSDescInGOTAndTransformToDelta32,
359
360 /// A TLVP entry getter/constructor, transformed to
361 /// Delta32ToTLVPLoadREXRelaxable.
362 ///
363 /// Indicates that this edge should be transformed into a
364 /// Delta32ToTLVPLoadREXRelaxable targeting the TLVP entry for the edge's
365 /// current target. A TLVP entry for the target should be created if one does
366 /// not already exist.
367 ///
368 /// Fixup expression:
369 /// NONE
370 ///
371 /// Errors:
372 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
373 /// phase will result in an assert/unreachable during the fixup phase.
374 ///
375 RequestTLVPAndTransformToPCRel32TLVPLoadREXRelaxable,
376 // First platform specific relocation.
377 FirstPlatformRelocation
378};
379
380/// Returns a string name for the given x86-64 edge. For debugging purposes
381/// only.
382const char *getEdgeKindName(Edge::Kind K);
383
384/// Returns true if the given uint64_t value is in range for a uint32_t.
385inline bool isInRangeForImmU32(uint64_t Value) {
386 return Value <= std::numeric_limits<uint32_t>::max();
387}
388
389/// Returns true if the given int64_t value is in range for an int32_t.
390inline bool isInRangeForImmS32(int64_t Value) {
391 return (Value >= std::numeric_limits<int32_t>::min() &&
392 Value <= std::numeric_limits<int32_t>::max());
393}
394
395/// Apply fixup expression for edge to block content.
396inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
397 const Symbol *GOTSymbol) {
398 using namespace support;
399
400 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
401 char *FixupPtr = BlockWorkingMem + E.getOffset();
402 auto FixupAddress = B.getAddress() + E.getOffset();
403
404 switch (E.getKind()) {
405
406 case Pointer64: {
407 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
408 *(ulittle64_t *)FixupPtr = Value;
409 break;
410 }
411
412 case Pointer32: {
413 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
414 if (LLVM_LIKELY(isInRangeForImmU32(Value)))
415 *(ulittle32_t *)FixupPtr = Value;
416 else
417 return makeTargetOutOfRangeError(G, B, E);
418 break;
419 }
420 case Pointer32Signed: {
421 int64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
422 if (LLVM_LIKELY(isInRangeForImmS32(Value)))
423 *(little32_t *)FixupPtr = Value;
424 else
425 return makeTargetOutOfRangeError(G, B, E);
426 break;
427 }
428
429 case Pointer16: {
430 uint64_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
431 if (LLVM_LIKELY(isUInt<16>(Value)))
432 *(ulittle16_t *)FixupPtr = Value;
433 else
434 return makeTargetOutOfRangeError(G, B, E);
435 break;
436 }
437
438 case PCRel32:
439 case BranchPCRel32:
440 case BranchPCRel32ToPtrJumpStub:
441 case BranchPCRel32ToPtrJumpStubBypassable:
442 case PCRel32GOTLoadRelaxable:
443 case PCRel32GOTLoadREXRelaxable:
444 case PCRel32TLVPLoadREXRelaxable: {
445 int64_t Value =
446 E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend();
447 if (LLVM_LIKELY(isInRangeForImmS32(Value)))
448 *(little32_t *)FixupPtr = Value;
449 else
450 return makeTargetOutOfRangeError(G, B, E);
451 break;
452 }
453
454 case Delta64: {
455 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
456 *(little64_t *)FixupPtr = Value;
457 break;
458 }
459
460 case Delta32: {
461 int64_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
462 if (LLVM_LIKELY(isInRangeForImmS32(Value)))
463 *(little32_t *)FixupPtr = Value;
464 else
465 return makeTargetOutOfRangeError(G, B, E);
466 break;
467 }
468
469 case NegDelta64: {
470 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
471 *(little64_t *)FixupPtr = Value;
472 break;
473 }
474
475 case NegDelta32: {
476 int64_t Value = FixupAddress - E.getTarget().getAddress() + E.getAddend();
477 if (LLVM_LIKELY(isInRangeForImmS32(Value)))
478 *(little32_t *)FixupPtr = Value;
479 else
480 return makeTargetOutOfRangeError(G, B, E);
481 break;
482 }
483 case Delta64FromGOT: {
484 assert(GOTSymbol && "No GOT section symbol");
485 int64_t Value =
486 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend();
487 *(little64_t *)FixupPtr = Value;
488 break;
489 }
490
491 default:
492 return make_error<JITLinkError>(
493 "In graph " + G.getName() + ", section " + B.getSection().getName() +
494 "unsupported edge kind" + getEdgeKindName(E.getKind()));
495 }
496
497 return Error::success();
498}
499
500/// x86_64 pointer size.
501constexpr uint64_t PointerSize = 8;
502
503/// x86-64 null pointer content.
504extern const char NullPointerContent[PointerSize];
505
506/// x86-64 pointer jump stub content.
507///
508/// Contains the instruction sequence for an indirect jump via an in-memory
509/// pointer:
510/// jmpq *ptr(%rip)
511extern const char PointerJumpStubContent[6];
512
513/// Creates a new pointer block in the given section and returns an anonymous
514/// symbol pointing to it.
515///
516/// If InitialTarget is given then an Pointer64 relocation will be added to the
517/// block pointing at InitialTarget.
518///
519/// The pointer block will have the following default values:
520/// alignment: 64-bit
521/// alignment-offset: 0
522/// address: highest allowable (~7U)
523inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
524 Symbol *InitialTarget = nullptr,
525 uint64_t InitialAddend = 0) {
526 auto &B = G.createContentBlock(PointerSection, NullPointerContent,
527 orc::ExecutorAddr(~uint64_t(7)), 8, 0);
528 if (InitialTarget)
529 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
530 return G.addAnonymousSymbol(B, 0, 8, false, false);
531}
532
533/// Create a jump stub block that jumps via the pointer at the given symbol.
534///
535/// The stub block will have the following default values:
536/// alignment: 8-bit
537/// alignment-offset: 0
538/// address: highest allowable: (~5U)
539inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
540 Symbol &PointerSymbol) {
541 auto &B = G.createContentBlock(StubSection, PointerJumpStubContent,
542 orc::ExecutorAddr(~uint64_t(5)), 1, 0);
543 B.addEdge(Delta32, 2, PointerSymbol, -4);
544 return B;
545}
546
547/// Create a jump stub that jumps via the pointer at the given symbol and
548/// an anonymous symbol pointing to it. Return the anonymous symbol.
549///
550/// The stub block will be created by createPointerJumpStubBlock.
551inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
552 Section &StubSection,
553 Symbol &PointerSymbol) {
554 return G.addAnonymousSymbol(
555 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true,
556 false);
557}
558
559/// Global Offset Table Builder.
560class GOTTableManager : public TableManager<GOTTableManager> {
561public:
562 static StringRef getSectionName() { return "$__GOT"; }
563
564 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
565 Edge::Kind KindToSet = Edge::Invalid;
566 switch (E.getKind()) {
567 case x86_64::Delta64FromGOT: {
568 // we need to make sure that the GOT section exists, but don't otherwise
569 // need to fix up this edge
570 getGOTSection(G);
571 return false;
572 }
573 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadREXRelaxable:
574 KindToSet = x86_64::PCRel32GOTLoadREXRelaxable;
575 break;
576 case x86_64::RequestGOTAndTransformToPCRel32GOTLoadRelaxable:
577 KindToSet = x86_64::PCRel32GOTLoadRelaxable;
578 break;
579 case x86_64::RequestGOTAndTransformToDelta64:
580 KindToSet = x86_64::Delta64;
581 break;
582 case x86_64::RequestGOTAndTransformToDelta64FromGOT:
583 KindToSet = x86_64::Delta64FromGOT;
584 break;
585 case x86_64::RequestGOTAndTransformToDelta32:
586 KindToSet = x86_64::Delta32;
587 break;
588 default:
589 return false;
590 }
591 assert(KindToSet != Edge::Invalid &&
592 "Fell through switch, but no new kind to set");
593 DEBUG_WITH_TYPE("jitlink", {
594 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
595 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
596 << formatv("{0:x}", E.getOffset()) << ")\n";
597 });
598 E.setKind(KindToSet);
599 E.setTarget(getEntryForTarget(G, E.getTarget()));
600 return true;
601 }
602
603 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
604 return createAnonymousPointer(G, getGOTSection(G), &Target);
605 }
606
607private:
608 Section &getGOTSection(LinkGraph &G) {
609 if (!GOTSection)
610 GOTSection = &G.createSection(getSectionName(), MemProt::Read);
611 return *GOTSection;
612 }
613
614 Section *GOTSection = nullptr;
615};
616
617/// Procedure Linkage Table Builder.
618class PLTTableManager : public TableManager<PLTTableManager> {
619public:
620 PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {}
621
622 static StringRef getSectionName() { return "$__STUBS"; }
623
624 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
625 if (E.getKind() == x86_64::BranchPCRel32 && !E.getTarget().isDefined()) {
626 DEBUG_WITH_TYPE("jitlink", {
627 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
628 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
629 << formatv("{0:x}", E.getOffset()) << ")\n";
630 });
631 // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to
632 // be optimized when the target is in-range.
633 E.setKind(x86_64::BranchPCRel32ToPtrJumpStubBypassable);
634 E.setTarget(getEntryForTarget(G, E.getTarget()));
635 return true;
636 }
637 return false;
638 }
639
640 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
641 return createAnonymousPointerJumpStub(G, getStubsSection(G),
642 GOT.getEntryForTarget(G, Target));
643 }
644
645public:
646 Section &getStubsSection(LinkGraph &G) {
647 if (!PLTSection)
648 PLTSection =
649 &G.createSection(getSectionName(), MemProt::Read | MemProt::Exec);
650 return *PLTSection;
651 }
652
653 GOTTableManager &GOT;
654 Section *PLTSection = nullptr;
655};
656
657/// Optimize the GOT and Stub relocations if the edge target address is in range
658/// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range,
659/// then replace GOT load with lea
660/// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is
661/// in range, replace a indirect jump by plt stub with a direct jump to the
662/// target
663Error optimizeGOTAndStubAccesses(LinkGraph &G);
664
665} // namespace x86_64
666} // end namespace jitlink
667} // end namespace llvm
668
669#endif // LLVM_EXECUTIONENGINE_JITLINK_X86_64_H
670

source code of llvm/include/llvm/ExecutionEngine/JITLink/x86_64.h