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
16#include "llvm/ExecutionEngine/JITLink/JITLink.h"
17#include "llvm/ExecutionEngine/JITLink/TableManager.h"
18#include "llvm/Support/Compiler.h"
19
20namespace llvm::jitlink::x86 {
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 ///
48 PCRel32,
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 ///
59 Pointer16,
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 ///
73 PCRel16,
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.
85 Delta32,
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.
97 Delta32FromGOT,
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
117 RequestGOTAndTransformToDelta32FromGOT,
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 ///
131 BranchPCRel32,
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 ///
160 BranchPCRel32ToPtrJumpStub,
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 ///
176 BranchPCRel32ToPtrJumpStubBypassable,
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.
184inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
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:
238 case BranchPCRel32ToPtrJumpStub:
239 case BranchPCRel32ToPtrJumpStubBypassable: {
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 Args: "In graph " + G.getName() + ", section " + B.getSection().getName() +
248 " unsupported edge kind " + getEdgeKindName(K: 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)
277inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
278 Symbol *InitialTarget = nullptr,
279 uint64_t InitialAddend = 0) {
280 auto &B = G.createContentBlock(Parent&: PointerSection, Content: NullPointerContent,
281 Address: orc::ExecutorAddr(), Alignment: 8, AlignmentOffset: 0);
282 if (InitialTarget)
283 B.addEdge(K: Pointer32, Offset: 0, Target&: *InitialTarget, Addend: InitialAddend);
284 return G.addAnonymousSymbol(Content&: B, Offset: 0, Size: PointerSize, IsCallable: false, IsLive: 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)
293inline Block &createPointerJumpStubBlock(LinkGraph &G, Section &StubSection,
294 Symbol &PointerSymbol) {
295 auto &B = G.createContentBlock(Parent&: StubSection, Content: PointerJumpStubContent,
296 Address: orc::ExecutorAddr(), Alignment: 8, AlignmentOffset: 0);
297 B.addEdge(K: 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 Offset: 2, Target&: PointerSymbol, Addend: 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.
309inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
310 Section &StubSection,
311 Symbol &PointerSymbol) {
312 return G.addAnonymousSymbol(
313 Content&: createPointerJumpStubBlock(G, StubSection, PointerSymbol), Offset: 0, Size: 6, IsCallable: true,
314 IsLive: false);
315}
316
317/// Global Offset Table Builder.
318class GOTTableManager : public TableManager<GOTTableManager> {
319public:
320 static StringRef getSectionName() { return "$__GOT"; }
321
322 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
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 }
331 case RequestGOTAndTransformToDelta32FromGOT:
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, Target&: E.getTarget()));
346 return true;
347 }
348
349 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
350 return createAnonymousPointer(G, PointerSection&: getGOTSection(G), InitialTarget: &Target);
351 }
352
353private:
354 Section &getGOTSection(LinkGraph &G) {
355 if (!GOTSection)
356 GOTSection = &G.createSection(Name: getSectionName(), Prot: 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:
366 PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {}
367
368 static StringRef getSectionName() { return "$__STUBS"; }
369
370 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
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.
379 E.setKind(BranchPCRel32ToPtrJumpStubBypassable);
380 E.setTarget(getEntryForTarget(G, Target&: E.getTarget()));
381 return true;
382 }
383 return false;
384 }
385
386 Symbol &createEntry(LinkGraph &G, Symbol &Target) {
387 return createAnonymousPointerJumpStub(G, StubSection&: getStubsSection(G),
388 PointerSymbol&: GOT.getEntryForTarget(G, Target));
389 }
390
391public:
392 Section &getStubsSection(LinkGraph &G) {
393 if (!PLTSection)
394 PLTSection = &G.createSection(Name: getSectionName(),
395 Prot: orc::MemProt::Read | orc::MemProt::Exec);
396 return *PLTSection;
397 }
398
399 GOTTableManager &GOT;
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
409LLVM_ABI Error optimizeGOTAndStubAccesses(LinkGraph &G);
410
411} // namespace llvm::jitlink::x86
412
413#endif // LLVM_EXECUTIONENGINE_JITLINK_X86_H
414