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