Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
14 | pmbaty | 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 | |||
129 | /// Returns a string name for the given i386 edge. For debugging purposes |
||
130 | /// only |
||
131 | const char *getEdgeKindName(Edge::Kind K); |
||
132 | |||
133 | /// Returns true if the given uint32_t value is in range for a uint16_t. |
||
134 | inline bool isInRangeForImmU16(uint32_t Value) { |
||
135 | return Value <= std::numeric_limits<uint16_t>::max(); |
||
136 | } |
||
137 | |||
138 | /// Returns true if the given int32_t value is in range for an int16_t. |
||
139 | inline bool isInRangeForImmS16(int32_t Value) { |
||
140 | return (Value >= std::numeric_limits<int16_t>::min() && |
||
141 | Value <= std::numeric_limits<int16_t>::max()); |
||
142 | } |
||
143 | |||
144 | /// Apply fixup expression for edge to block content. |
||
145 | inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, |
||
146 | const Symbol *GOTSymbol) { |
||
147 | using namespace i386; |
||
148 | using namespace llvm::support; |
||
149 | |||
150 | char *BlockWorkingMem = B.getAlreadyMutableContent().data(); |
||
151 | char *FixupPtr = BlockWorkingMem + E.getOffset(); |
||
152 | auto FixupAddress = B.getAddress() + E.getOffset(); |
||
153 | |||
154 | switch (E.getKind()) { |
||
155 | case i386::None: { |
||
156 | break; |
||
157 | } |
||
158 | |||
159 | case i386::Pointer32: { |
||
160 | uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); |
||
161 | *(ulittle32_t *)FixupPtr = Value; |
||
162 | break; |
||
163 | } |
||
164 | |||
165 | case i386::PCRel32: { |
||
166 | int32_t Value = |
||
167 | E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); |
||
168 | *(little32_t *)FixupPtr = Value; |
||
169 | break; |
||
170 | } |
||
171 | |||
172 | case i386::Pointer16: { |
||
173 | uint32_t Value = E.getTarget().getAddress().getValue() + E.getAddend(); |
||
174 | if (LLVM_LIKELY(isInRangeForImmU16(Value))) |
||
175 | *(ulittle16_t *)FixupPtr = Value; |
||
176 | else |
||
177 | return makeTargetOutOfRangeError(G, B, E); |
||
178 | break; |
||
179 | } |
||
180 | |||
181 | case i386::PCRel16: { |
||
182 | int32_t Value = |
||
183 | E.getTarget().getAddress() - (FixupAddress + 4) + E.getAddend(); |
||
184 | if (LLVM_LIKELY(isInRangeForImmS16(Value))) |
||
185 | *(little16_t *)FixupPtr = Value; |
||
186 | else |
||
187 | return makeTargetOutOfRangeError(G, B, E); |
||
188 | break; |
||
189 | } |
||
190 | |||
191 | case i386::Delta32: { |
||
192 | int32_t Value = E.getTarget().getAddress() - FixupAddress + E.getAddend(); |
||
193 | *(little32_t *)FixupPtr = Value; |
||
194 | break; |
||
195 | } |
||
196 | |||
197 | case i386::Delta32FromGOT: { |
||
198 | assert(GOTSymbol && "No GOT section symbol"); |
||
199 | int32_t Value = |
||
200 | E.getTarget().getAddress() - GOTSymbol->getAddress() + E.getAddend(); |
||
201 | *(little32_t *)FixupPtr = Value; |
||
202 | break; |
||
203 | } |
||
204 | |||
205 | default: |
||
206 | return make_error<JITLinkError>( |
||
207 | "In graph " + G.getName() + ", section " + B.getSection().getName() + |
||
208 | "unsupported edge kind" + getEdgeKindName(E.getKind())); |
||
209 | } |
||
210 | |||
211 | return Error::success(); |
||
212 | } |
||
213 | |||
214 | /// i386 pointer size. |
||
215 | constexpr uint32_t PointerSize = 4; |
||
216 | |||
217 | /// i386 null pointer content. |
||
218 | extern const char NullPointerContent[PointerSize]; |
||
219 | |||
220 | /// Creates a new pointer block in the given section and returns an anonymous |
||
221 | /// symbol pointing to it. |
||
222 | /// |
||
223 | /// If InitialTarget is given then an Pointer32 relocation will be added to the |
||
224 | /// block pointing at InitialTarget. |
||
225 | /// |
||
226 | /// The pointer block will have the following default values: |
||
227 | /// alignment: 32-bit |
||
228 | /// alignment-offset: 0 |
||
229 | /// address: highest allowable (~7U) |
||
230 | inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, |
||
231 | Symbol *InitialTarget = nullptr, |
||
232 | uint64_t InitialAddend = 0) { |
||
233 | auto &B = G.createContentBlock(PointerSection, NullPointerContent, |
||
234 | orc::ExecutorAddr(), 8, 0); |
||
235 | if (InitialTarget) |
||
236 | B.addEdge(Pointer32, 0, *InitialTarget, InitialAddend); |
||
237 | return G.addAnonymousSymbol(B, 0, PointerSize, false, false); |
||
238 | } |
||
239 | |||
240 | /// Global Offset Table Builder. |
||
241 | class GOTTableManager : public TableManager<GOTTableManager> { |
||
242 | public: |
||
243 | static StringRef getSectionName() { return "$__GOT"; } |
||
244 | |||
245 | bool visitEdge(LinkGraph &G, Block *B, Edge &E) { |
||
246 | Edge::Kind KindToSet = Edge::Invalid; |
||
247 | switch (E.getKind()) { |
||
248 | case i386::Delta32FromGOT: { |
||
249 | // we need to make sure that the GOT section exists, but don't otherwise |
||
250 | // need to fix up this edge |
||
251 | getGOTSection(G); |
||
252 | return false; |
||
253 | } |
||
254 | case i386::RequestGOTAndTransformToDelta32FromGOT: |
||
255 | KindToSet = i386::Delta32FromGOT; |
||
256 | break; |
||
257 | default: |
||
258 | return false; |
||
259 | } |
||
260 | assert(KindToSet != Edge::Invalid && |
||
261 | "Fell through switch, but no new kind to set"); |
||
262 | DEBUG_WITH_TYPE("jitlink", { |
||
263 | dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at " |
||
264 | << B->getFixupAddress(E) << " (" << B->getAddress() << " + " |
||
265 | << formatv("{0:x}", E.getOffset()) << ")\n"; |
||
266 | }); |
||
267 | E.setKind(KindToSet); |
||
268 | E.setTarget(getEntryForTarget(G, E.getTarget())); |
||
269 | return true; |
||
270 | } |
||
271 | |||
272 | Symbol &createEntry(LinkGraph &G, Symbol &Target) { |
||
273 | return createAnonymousPointer(G, getGOTSection(G), &Target); |
||
274 | } |
||
275 | |||
276 | private: |
||
277 | Section &getGOTSection(LinkGraph &G) { |
||
278 | if (!GOTSection) |
||
279 | GOTSection = &G.createSection(getSectionName(), orc::MemProt::Read); |
||
280 | return *GOTSection; |
||
281 | } |
||
282 | |||
283 | Section *GOTSection = nullptr; |
||
284 | }; |
||
285 | |||
286 | } // namespace llvm::jitlink::i386 |
||
287 | |||
288 | #endif // LLVM_EXECUTIONENGINE_JITLINK_I386_H |