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