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