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