LLVM 22.0.0git
x86.h
Go to the documentation of this file.
1//===----- x86.h - Generic JITLink x86 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 objects.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_JITLINK_X86_H
14#define LLVM_EXECUTIONENGINE_JITLINK_X86_H
15
19
21
22/// Represets x86 fixups
23enum EdgeKind_x86 : Edge::Kind {
24
25 /// A plain 32-bit pointer value relocation.
26 ///
27 /// Fixup expression:
28 /// Fixup <- Target + Addend : uint32
29 ///
30 /// Errors:
31 /// - The target must reside in the low 32-bits of the address space,
32 /// otherwise an out-of-range error will be returned.
33 ///
34 Pointer32 = Edge::FirstRelocation,
35
36 /// A 32-bit PC-relative relocation.
37 ///
38 /// Represents a data/control flow instruction using PC-relative addressing
39 /// to a target.
40 ///
41 /// Fixup expression:
42 /// Fixup <- Target - Fixup + Addend : int32
43 ///
44 /// Errors:
45 /// - The result of the fixup expression must fit into an int32, otherwise
46 /// an out-of-range error will be returned.
47 ///
49
50 /// A plain 16-bit pointer value relocation.
51 ///
52 /// Fixup expression:
53 /// Fixup <- Target + Addend : uint16
54 ///
55 /// Errors:
56 /// - The target must reside in the low 16-bits of the address space,
57 /// otherwise an out-of-range error will be returned.
58 ///
60
61 /// A 16-bit PC-relative relocation.
62 ///
63 /// Represents a data/control flow instruction using PC-relative addressing
64 /// to a target.
65 ///
66 /// Fixup expression:
67 /// Fixup <- Target - Fixup + Addend : int16
68 ///
69 /// Errors:
70 /// - The result of the fixup expression must fit into an int16, otherwise
71 /// an out-of-range error will be returned.
72 ///
74
75 /// A 32-bit delta.
76 ///
77 /// Delta from the fixup to the target.
78 ///
79 /// Fixup expression:
80 /// Fixup <- Target - Fixup + Addend : int32
81 ///
82 /// Errors:
83 /// - The result of the fixup expression must fit into an int32, otherwise
84 /// an out-of-range error will be returned.
86
87 /// A 32-bit GOT delta.
88 ///
89 /// Delta from the global offset table to the target.
90 ///
91 /// Fixup expression:
92 /// Fixup <- Target - GOTSymbol + Addend : int32
93 ///
94 /// Errors:
95 /// - *ASSERTION* Failure to a null pointer GOTSymbol, which the GOT section
96 /// symbol was not been defined.
98
99 /// A GOT entry offset within GOT getter/constructor, transformed to
100 /// Delta32FromGOT pointing at the GOT entry for the original target.
101 ///
102 /// Indicates that this edge should be transformed into a Delta32FromGOT
103 /// targeting the GOT entry for the edge's current target, maintaining the
104 /// same addend.
105 /// A GOT entry for the target should be created if one does not already
106 /// exist.
107 ///
108 /// Edges of this kind are usually handled by a GOT builder pass inserted by
109 /// default
110 ///
111 /// Fixup expression:
112 /// NONE
113 ///
114 /// Errors:
115 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
116 /// phase will result in an assert/unreachable during the fixup phase
118
119 /// A 32-bit PC-relative branch.
120 ///
121 /// Represents a PC-relative call or branch to a target. This can be used to
122 /// identify, record, and/or patch call sites.
123 ///
124 /// Fixup expression:
125 /// Fixup <- Target - Fixup + Addend : int32
126 ///
127 /// Errors:
128 /// - The result of the fixup expression must fit into an int32, otherwise
129 /// an out-of-range error will be returned.
130 ///
132
133 /// A 32-bit PC-relative branch to a pointer jump stub.
134 ///
135 /// The target of this relocation should be a pointer jump stub of the form:
136 ///
137 /// \code{.s}
138 /// .text
139 /// jmp *tgtptr
140 /// ; ...
141 ///
142 /// .data
143 /// tgtptr:
144 /// .quad 0
145 /// \endcode
146 ///
147 /// This edge kind has the same fixup expression as BranchPCRel32, but further
148 /// identifies the call/branch as being to a pointer jump stub. For edges of
149 /// this kind the jump stub should not be bypassed (use
150 /// BranchPCRel32ToPtrJumpStubBypassable for that), but the pointer location
151 /// target may be recorded to allow manipulation at runtime.
152 ///
153 /// Fixup expression:
154 /// Fixup <- Target - Fixup + Addend : int32
155 ///
156 /// Errors:
157 /// - The result of the fixup expression must fit into an int32, otherwise
158 /// an out-of-range error will be returned.
159 ///
161
162 /// A relaxable version of BranchPCRel32ToPtrJumpStub.
163 ///
164 /// The edge kind has the same fixup expression as BranchPCRel32ToPtrJumpStub,
165 /// but identifies the call/branch as being to a pointer jump stub that may be
166 /// bypassed with a direct jump to the ultimate target if the ultimate target
167 /// is within range of the fixup location.
168 ///
169 /// Fixup expression:
170 /// Fixup <- Target - Fixup + Addend : int32
171 ///
172 /// Errors:
173 /// - The result of the fixup expression must fit into an int32, otherwise
174 /// an out-of-range error will be returned.
175 ///
177};
178
179/// Returns a string name for the given x86 edge. For debugging purposes
180/// only
181LLVM_ABI const char *getEdgeKindName(Edge::Kind K);
182
183/// Apply fixup expression for edge to block content.
185 const Symbol *GOTSymbol) {
186 using namespace llvm::support;
187
188 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
189 char *FixupPtr = BlockWorkingMem + E.getOffset();
190 auto FixupAddress = B.getAddress() + E.getOffset();
191
192 switch (E.getKind()) {
193 case Pointer32: {
194 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
195 *(ulittle32_t *)FixupPtr = Value;
196 break;
197 }
198
199 case PCRel32: {
200 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
201 *(little32_t *)FixupPtr = Value;
202 break;
203 }
204
205 case Pointer16: {
206 uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend();
207 if (LLVM_LIKELY(isUInt<16>(Value)))
208 *(ulittle16_t *)FixupPtr = Value;
209 else
210 return makeTargetOutOfRangeError(G, B, E);
211 break;
212 }
213
214 case PCRel16: {
215 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
216 if (LLVM_LIKELY(isInt<16>(Value)))
217 *(little16_t *)FixupPtr = Value;
218 else
219 return makeTargetOutOfRangeError(G, B, E);
220 break;
221 }
222
223 case Delta32: {
224 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
225 *(little32_t *)FixupPtr = Value;
226 break;
227 }
228
229 case Delta32FromGOT: {
230 assert(GOTSymbol && "No GOT section symbol");
231 int32_t Value =
232 E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend();
233 *(little32_t *)FixupPtr = Value;
234 break;
235 }
236
237 case BranchPCRel32:
240 int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend();
241 *(little32_t *)FixupPtr = Value;
242 break;
243 }
244
245 default:
246 return make_error<JITLinkError>(
247 "In graph " + G.getName() + ", section " + B.getSection().getName() +
248 " unsupported edge kind " + getEdgeKindName(E.getKind()));
249 }
250
251 return Error::success();
252}
253
254/// x86 pointer size.
255constexpr uint32_t PointerSize = 4;
256
257/// x86 null pointer content.
258LLVM_ABI extern const char NullPointerContent[PointerSize];
259
260/// x86 pointer jump stub content.
261///
262/// Contains the instruction sequence for an indirect jump via an in-memory
263/// pointer:
264/// jmpq *ptr
265LLVM_ABI extern const char PointerJumpStubContent[6];
266
267/// Creates a new pointer block in the given section and returns an anonymous
268/// symbol pointing to it.
269///
270/// If InitialTarget is given then an Pointer32 relocation will be added to the
271/// block pointing at InitialTarget.
272///
273/// The pointer block will have the following default values:
274/// alignment: 32-bit
275/// alignment-offset: 0
276/// address: highest allowable (~7U)
278 Symbol *InitialTarget = nullptr,
279 uint64_t InitialAddend = 0) {
280 auto &B = G.createContentBlock(PointerSection, NullPointerContent,
281 orc::ExecutorAddr(), 8, 0);
282 if (InitialTarget)
283 B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend);
284 return G.addAnonymousSymbol(B, 0, PointerSize, false, false);
285}
286
287/// Create a jump stub block that jumps via the pointer at the given symbol.
288///
289/// The stub block will have the following default values:
290/// alignment: 8-bit
291/// alignment-offset: 0
292/// address: highest allowable: (~5U)
294 Symbol &PointerSymbol) {
295 auto &B = G.createContentBlock(StubSection, PointerJumpStubContent,
296 orc::ExecutorAddr(), 8, 0);
297 B.addEdge(Pointer32,
298 // Offset is 2 because the the first 2 bytes of the
299 // jump stub block are {0xff, 0x25} -- an indirect absolute
300 // jump.
301 2, PointerSymbol, 0);
302 return B;
303}
304
305/// Create a jump stub that jumps via the pointer at the given symbol and
306/// an anonymous symbol pointing to it. Return the anonymous symbol.
307///
308/// The stub block will be created by createPointerJumpStubBlock.
310 Section &StubSection,
311 Symbol &PointerSymbol) {
312 return G.addAnonymousSymbol(
313 createPointerJumpStubBlock(G, StubSection, PointerSymbol), 0, 6, true,
314 false);
315}
316
317/// Global Offset Table Builder.
318class GOTTableManager : public TableManager<GOTTableManager> {
319public:
320 static StringRef getSectionName() { return "$__GOT"; }
321
323 Edge::Kind KindToSet = Edge::Invalid;
324 switch (E.getKind()) {
325 case Delta32FromGOT: {
326 // we need to make sure that the GOT section exists, but don't otherwise
327 // need to fix up this edge
328 getGOTSection(G);
329 return false;
330 }
332 KindToSet = Delta32FromGOT;
333 break;
334 default:
335 return false;
336 }
337 assert(KindToSet != Edge::Invalid &&
338 "Fell through switch, but no new kind to set");
339 DEBUG_WITH_TYPE("jitlink", {
340 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
341 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
342 << formatv("{0:x}", E.getOffset()) << ")\n";
343 });
344 E.setKind(KindToSet);
345 E.setTarget(getEntryForTarget(G, E.getTarget()));
346 return true;
347 }
348
350 return createAnonymousPointer(G, getGOTSection(G), &Target);
351 }
352
353private:
354 Section &getGOTSection(LinkGraph &G) {
355 if (!GOTSection)
356 GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read);
357 return *GOTSection;
358 }
359
360 Section *GOTSection = nullptr;
361};
362
363/// Procedure Linkage Table Builder.
364class PLTTableManager : public TableManager<PLTTableManager> {
365public:
367
368 static StringRef getSectionName() { return "$__STUBS"; }
369
371 if (E.getKind() == BranchPCRel32 && !E.getTarget().isDefined()) {
372 DEBUG_WITH_TYPE("jitlink", {
373 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
374 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
375 << formatv("{0:x}", E.getOffset()) << ")\n";
376 });
377 // Set the edge kind to Branch32ToPtrJumpStubBypassable to enable it to
378 // be optimized when the target is in-range.
380 E.setTarget(getEntryForTarget(G, E.getTarget()));
381 return true;
382 }
383 return false;
384 }
385
389 }
390
391public:
393 if (!PLTSection)
394 PLTSection = &G.createSection(getSectionName(),
396 return *PLTSection;
397 }
398
400 Section *PLTSection = nullptr;
401};
402
403/// Optimize the GOT and Stub relocations if the edge target address is in range
404/// 1. PCRel32GOTLoadRelaxable. For this edge kind, if the target is in range,
405/// then replace GOT load with lea. (THIS IS UNIMPLEMENTED RIGHT NOW!)
406/// 2. BranchPCRel32ToPtrJumpStubRelaxable. For this edge kind, if the target is
407/// in range, replace a indirect jump by plt stub with a direct jump to the
408/// target
410
411} // namespace llvm::jitlink::x86
412
413#endif // LLVM_EXECUTIONENGINE_JITLINK_X86_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_ABI
Definition: Compiler.h:213
#define LLVM_LIKELY(EXPR)
Definition: Compiler.h:335
#define G(x, y, z)
Definition: MD5.cpp:56
#define DEBUG_WITH_TYPE(TYPE,...)
DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug information.
Definition: Debug.h:77
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
static ErrorSuccess success()
Create a success value.
Definition: Error.h:336
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition: Value.h:75
Represents an address in the executor process.
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:207