1 | //===------ aarch32.h - Generic JITLink arm/thumb 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 arm/thumb objects. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #ifndef LLVM_EXECUTIONENGINE_JITLINK_AARCH32 |
14 | #define LLVM_EXECUTIONENGINE_JITLINK_AARCH32 |
15 | |
16 | #include "TableManager.h" |
17 | #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
18 | #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" |
19 | #include "llvm/Support/ARMBuildAttributes.h" |
20 | #include "llvm/Support/Error.h" |
21 | |
22 | namespace llvm { |
23 | namespace jitlink { |
24 | namespace aarch32 { |
25 | |
26 | /// Check whether the given target flags are set for this Symbol. |
27 | bool hasTargetFlags(Symbol &Sym, TargetFlagsType Flags); |
28 | |
29 | /// JITLink-internal AArch32 fixup kinds |
30 | enum EdgeKind_aarch32 : Edge::Kind { |
31 | |
32 | /// |
33 | /// Relocations of class Data respect target endianness (unless otherwise |
34 | /// specified) |
35 | /// |
36 | FirstDataRelocation = Edge::FirstRelocation, |
37 | |
38 | /// Relative 32-bit value relocation |
39 | Data_Delta32 = FirstDataRelocation, |
40 | |
41 | /// Absolute 32-bit value relocation |
42 | Data_Pointer32, |
43 | |
44 | /// Relative 31-bit value relocation that preserves the most-significant bit |
45 | Data_PRel31, |
46 | |
47 | /// Create GOT entry and store offset |
48 | Data_RequestGOTAndTransformToDelta32, |
49 | |
50 | LastDataRelocation = Data_RequestGOTAndTransformToDelta32, |
51 | |
52 | /// |
53 | /// Relocations of class Arm (covers fixed-width 4-byte instruction subset) |
54 | /// |
55 | FirstArmRelocation, |
56 | |
57 | /// Write immediate value for unconditional PC-relative branch with link. |
58 | /// We patch the instruction opcode to account for an instruction-set state |
59 | /// switch: we use the bl instruction to stay in ARM and the blx instruction |
60 | /// to switch to Thumb. |
61 | Arm_Call = FirstArmRelocation, |
62 | |
63 | /// Write immediate value for conditional PC-relative branch without link. |
64 | /// If the branch target is not ARM, we are forced to generate an explicit |
65 | /// interworking stub. |
66 | Arm_Jump24, |
67 | |
68 | /// Write immediate value to the lower halfword of the destination register |
69 | Arm_MovwAbsNC, |
70 | |
71 | /// Write immediate value to the top halfword of the destination register |
72 | Arm_MovtAbs, |
73 | |
74 | LastArmRelocation = Arm_MovtAbs, |
75 | |
76 | /// |
77 | /// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset) |
78 | /// |
79 | FirstThumbRelocation, |
80 | |
81 | /// Write immediate value for unconditional PC-relative branch with link. |
82 | /// We patch the instruction opcode to account for an instruction-set state |
83 | /// switch: we use the bl instruction to stay in Thumb and the blx instruction |
84 | /// to switch to ARM. |
85 | Thumb_Call = FirstThumbRelocation, |
86 | |
87 | /// Write immediate value for PC-relative branch without link. The instruction |
88 | /// can be made conditional by an IT block. If the branch target is not ARM, |
89 | /// we are forced to generate an explicit interworking stub. |
90 | Thumb_Jump24, |
91 | |
92 | /// Write immediate value to the lower halfword of the destination register |
93 | Thumb_MovwAbsNC, |
94 | |
95 | /// Write immediate value to the top halfword of the destination register |
96 | Thumb_MovtAbs, |
97 | |
98 | /// Write PC-relative immediate value to the lower halfword of the destination |
99 | /// register |
100 | Thumb_MovwPrelNC, |
101 | |
102 | /// Write PC-relative immediate value to the top halfword of the destination |
103 | /// register |
104 | Thumb_MovtPrel, |
105 | |
106 | LastThumbRelocation = Thumb_MovtPrel, |
107 | |
108 | /// No-op relocation |
109 | None, |
110 | |
111 | LastRelocation = None, |
112 | }; |
113 | |
114 | /// Flags enum for AArch32-specific symbol properties |
115 | enum TargetFlags_aarch32 : TargetFlagsType { |
116 | ThumbSymbol = 1 << 0, |
117 | }; |
118 | |
119 | /// Human-readable name for a given CPU architecture kind |
120 | const char *getCPUArchName(ARMBuildAttrs::CPUArch K); |
121 | |
122 | /// Get a human-readable name for the given AArch32 edge kind. |
123 | const char *getEdgeKindName(Edge::Kind K); |
124 | |
125 | /// AArch32 uses stubs for a number of purposes, like branch range extension |
126 | /// or interworking between Arm and Thumb instruction subsets. |
127 | /// |
128 | /// Stub implementations vary depending on CPU architecture (v4, v6, v7), |
129 | /// instruction subset and branch type (absolute/PC-relative). |
130 | /// |
131 | /// For each kind of stub, the StubsFlavor defines one concrete form that is |
132 | /// used throughout the LinkGraph. |
133 | /// |
134 | /// Stubs are often called "veneers" in the official docs and online. |
135 | /// |
136 | enum class StubsFlavor { |
137 | Undefined = 0, |
138 | pre_v7, |
139 | v7, |
140 | }; |
141 | |
142 | /// JITLink sub-arch configuration for Arm CPU models |
143 | struct ArmConfig { |
144 | bool J1J2BranchEncoding = false; |
145 | StubsFlavor Stubs = StubsFlavor::Undefined; |
146 | // In the long term, we might want a linker switch like --target1-rel |
147 | bool Target1Rel = false; |
148 | }; |
149 | |
150 | /// Obtain the sub-arch configuration for a given Arm CPU model. |
151 | inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) { |
152 | ArmConfig ArmCfg; |
153 | if (CPUArch == ARMBuildAttrs::v7 || CPUArch >= ARMBuildAttrs::v7E_M) { |
154 | ArmCfg.J1J2BranchEncoding = true; |
155 | ArmCfg.Stubs = StubsFlavor::v7; |
156 | } else { |
157 | ArmCfg.J1J2BranchEncoding = false; |
158 | ArmCfg.Stubs = StubsFlavor::pre_v7; |
159 | } |
160 | return ArmCfg; |
161 | } |
162 | |
163 | /// Immutable pair of halfwords, Hi and Lo, with overflow check |
164 | struct HalfWords { |
165 | constexpr HalfWords() : Hi(0), Lo(0) {} |
166 | constexpr HalfWords(uint32_t Hi, uint32_t Lo) : Hi(Hi), Lo(Lo) { |
167 | assert(isUInt<16>(Hi) && "Overflow in first half-word" ); |
168 | assert(isUInt<16>(Lo) && "Overflow in second half-word" ); |
169 | } |
170 | const uint16_t Hi; // First halfword |
171 | const uint16_t Lo; // Second halfword |
172 | }; |
173 | |
174 | /// FixupInfo base class is required for dynamic lookups. |
175 | struct FixupInfoBase { |
176 | static const FixupInfoBase *getDynFixupInfo(Edge::Kind K); |
177 | virtual ~FixupInfoBase() {} |
178 | }; |
179 | |
180 | /// FixupInfo checks for Arm edge kinds work on 32-bit words |
181 | struct FixupInfoArm : public FixupInfoBase { |
182 | bool (*checkOpcode)(uint32_t Wd) = nullptr; |
183 | }; |
184 | |
185 | /// FixupInfo check for Thumb32 edge kinds work on a pair of 16-bit halfwords |
186 | struct FixupInfoThumb : public FixupInfoBase { |
187 | bool (*checkOpcode)(uint16_t Hi, uint16_t Lo) = nullptr; |
188 | }; |
189 | |
190 | /// Collection of named constants per fixup kind |
191 | /// |
192 | /// Mandatory entries: |
193 | /// Opcode - Values of the op-code bits in the instruction, with |
194 | /// unaffected bits nulled |
195 | /// OpcodeMask - Mask with all bits set that encode the op-code |
196 | /// |
197 | /// Other common entries: |
198 | /// ImmMask - Mask with all bits set that encode the immediate value |
199 | /// RegMask - Mask with all bits set that encode the register |
200 | /// |
201 | /// Specializations can add further custom fields without restrictions. |
202 | /// |
203 | template <EdgeKind_aarch32 Kind> struct FixupInfo {}; |
204 | |
205 | struct FixupInfoArmBranch : public FixupInfoArm { |
206 | static constexpr uint32_t Opcode = 0x0a000000; |
207 | static constexpr uint32_t ImmMask = 0x00ffffff; |
208 | }; |
209 | |
210 | template <> struct FixupInfo<Arm_Jump24> : public FixupInfoArmBranch { |
211 | static constexpr uint32_t OpcodeMask = 0x0f000000; |
212 | }; |
213 | |
214 | template <> struct FixupInfo<Arm_Call> : public FixupInfoArmBranch { |
215 | static constexpr uint32_t OpcodeMask = 0x0e000000; |
216 | static constexpr uint32_t CondMask = 0xe0000000; // excluding BLX bit |
217 | static constexpr uint32_t Unconditional = 0xe0000000; |
218 | static constexpr uint32_t BitH = 0x01000000; |
219 | static constexpr uint32_t BitBlx = 0x10000000; |
220 | }; |
221 | |
222 | struct FixupInfoArmMov : public FixupInfoArm { |
223 | static constexpr uint32_t OpcodeMask = 0x0ff00000; |
224 | static constexpr uint32_t ImmMask = 0x000f0fff; |
225 | static constexpr uint32_t RegMask = 0x0000f000; |
226 | }; |
227 | |
228 | template <> struct FixupInfo<Arm_MovtAbs> : public FixupInfoArmMov { |
229 | static constexpr uint32_t Opcode = 0x03400000; |
230 | }; |
231 | |
232 | template <> struct FixupInfo<Arm_MovwAbsNC> : public FixupInfoArmMov { |
233 | static constexpr uint32_t Opcode = 0x03000000; |
234 | }; |
235 | |
236 | template <> struct FixupInfo<Thumb_Jump24> : public FixupInfoThumb { |
237 | static constexpr HalfWords Opcode{0xf000, 0x9000}; |
238 | static constexpr HalfWords OpcodeMask{0xf800, 0x9000}; |
239 | static constexpr HalfWords ImmMask{0x07ff, 0x2fff}; |
240 | }; |
241 | |
242 | template <> struct FixupInfo<Thumb_Call> : public FixupInfoThumb { |
243 | static constexpr HalfWords Opcode{0xf000, 0xc000}; |
244 | static constexpr HalfWords OpcodeMask{0xf800, 0xc000}; |
245 | static constexpr HalfWords ImmMask{0x07ff, 0x2fff}; |
246 | static constexpr uint16_t LoBitH = 0x0001; |
247 | static constexpr uint16_t LoBitNoBlx = 0x1000; |
248 | }; |
249 | |
250 | struct FixupInfoThumbMov : public FixupInfoThumb { |
251 | static constexpr HalfWords OpcodeMask{0xfbf0, 0x8000}; |
252 | static constexpr HalfWords ImmMask{0x040f, 0x70ff}; |
253 | static constexpr HalfWords RegMask{0x0000, 0x0f00}; |
254 | }; |
255 | |
256 | template <> struct FixupInfo<Thumb_MovtAbs> : public FixupInfoThumbMov { |
257 | static constexpr HalfWords Opcode{0xf2c0, 0x0000}; |
258 | }; |
259 | |
260 | template <> struct FixupInfo<Thumb_MovtPrel> : public FixupInfoThumbMov { |
261 | static constexpr HalfWords Opcode{0xf2c0, 0x0000}; |
262 | }; |
263 | |
264 | template <> struct FixupInfo<Thumb_MovwAbsNC> : public FixupInfoThumbMov { |
265 | static constexpr HalfWords Opcode{0xf240, 0x0000}; |
266 | }; |
267 | |
268 | template <> struct FixupInfo<Thumb_MovwPrelNC> : public FixupInfoThumbMov { |
269 | static constexpr HalfWords Opcode{0xf240, 0x0000}; |
270 | }; |
271 | |
272 | /// Helper function to read the initial addend for Data-class relocations. |
273 | Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset, |
274 | Edge::Kind Kind); |
275 | |
276 | /// Helper function to read the initial addend for Arm-class relocations. |
277 | Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, Edge::OffsetT Offset, |
278 | Edge::Kind Kind); |
279 | |
280 | /// Helper function to read the initial addend for Thumb-class relocations. |
281 | Expected<int64_t> readAddendThumb(LinkGraph &G, Block &B, Edge::OffsetT Offset, |
282 | Edge::Kind Kind, const ArmConfig &ArmCfg); |
283 | |
284 | /// Read the initial addend for a REL-type relocation. It's the value encoded |
285 | /// in the immediate field of the fixup location by the compiler. |
286 | inline Expected<int64_t> readAddend(LinkGraph &G, Block &B, |
287 | Edge::OffsetT Offset, Edge::Kind Kind, |
288 | const ArmConfig &ArmCfg) { |
289 | if (Kind <= LastDataRelocation) |
290 | return readAddendData(G, B, Offset, Kind); |
291 | |
292 | if (Kind <= LastArmRelocation) |
293 | return readAddendArm(G, B, Offset, Kind); |
294 | |
295 | if (Kind <= LastThumbRelocation) |
296 | return readAddendThumb(G, B, Offset, Kind, ArmCfg); |
297 | |
298 | assert(Kind == None && "Not associated with a relocation class" ); |
299 | return 0; |
300 | } |
301 | |
302 | /// Helper function to apply the fixup for Data-class relocations. |
303 | Error applyFixupData(LinkGraph &G, Block &B, const Edge &E); |
304 | |
305 | /// Helper function to apply the fixup for Arm-class relocations. |
306 | Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E); |
307 | |
308 | /// Helper function to apply the fixup for Thumb-class relocations. |
309 | Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E, |
310 | const ArmConfig &ArmCfg); |
311 | |
312 | /// Apply fixup expression for edge to block content. |
313 | inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, |
314 | const ArmConfig &ArmCfg) { |
315 | Edge::Kind Kind = E.getKind(); |
316 | |
317 | if (Kind <= LastDataRelocation) |
318 | return applyFixupData(G, B, E); |
319 | |
320 | if (Kind <= LastArmRelocation) |
321 | return applyFixupArm(G, B, E); |
322 | |
323 | if (Kind <= LastThumbRelocation) |
324 | return applyFixupThumb(G, B, E, ArmCfg); |
325 | |
326 | assert(Kind == None && "Not associated with a relocation class" ); |
327 | return Error::success(); |
328 | } |
329 | |
330 | /// Populate a Global Offset Table from edges that request it. |
331 | class GOTBuilder : public TableManager<GOTBuilder> { |
332 | public: |
333 | static StringRef getSectionName() { return "$__GOT" ; } |
334 | |
335 | bool visitEdge(LinkGraph &G, Block *B, Edge &E); |
336 | Symbol &createEntry(LinkGraph &G, Symbol &Target); |
337 | |
338 | private: |
339 | Section *GOTSection = nullptr; |
340 | }; |
341 | |
342 | /// Stubs builder emits non-position-independent Arm stubs for pre-v7 CPUs. |
343 | /// These architectures have no MovT/MovW instructions and don't support Thumb2. |
344 | /// BL is the only Thumb instruction that can generate stubs and they can always |
345 | /// be transformed into BLX. |
346 | class StubsManager_prev7 { |
347 | public: |
348 | StubsManager_prev7() = default; |
349 | |
350 | /// Name of the object file section that will contain all our stubs. |
351 | static StringRef getSectionName() { |
352 | return "__llvm_jitlink_aarch32_STUBS_prev7" ; |
353 | } |
354 | |
355 | /// Implements link-graph traversal via visitExistingEdges() |
356 | bool visitEdge(LinkGraph &G, Block *B, Edge &E); |
357 | |
358 | private: |
359 | // Each stub uses a single block that can have 2 entryponts, one for Arm and |
360 | // one for Thumb |
361 | struct StubMapEntry { |
362 | Block *B = nullptr; |
363 | Symbol *ArmEntry = nullptr; |
364 | Symbol *ThumbEntry = nullptr; |
365 | }; |
366 | |
367 | std::pair<StubMapEntry *, bool> getStubMapSlot(StringRef Name) { |
368 | auto &&[Stubs, NewStub] = StubMap.try_emplace(Key: Name); |
369 | return std::make_pair(x: &Stubs->second, y&: NewStub); |
370 | } |
371 | |
372 | Symbol *getOrCreateSlotEntrypoint(LinkGraph &G, StubMapEntry &Slot, |
373 | bool Thumb); |
374 | |
375 | DenseMap<StringRef, StubMapEntry> StubMap; |
376 | Section *StubsSection = nullptr; |
377 | }; |
378 | |
379 | /// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs. |
380 | class StubsManager_v7 { |
381 | public: |
382 | StubsManager_v7() = default; |
383 | |
384 | /// Name of the object file section that will contain all our stubs. |
385 | static StringRef getSectionName() { |
386 | return "__llvm_jitlink_aarch32_STUBS_v7" ; |
387 | } |
388 | |
389 | /// Implements link-graph traversal via visitExistingEdges(). |
390 | bool visitEdge(LinkGraph &G, Block *B, Edge &E); |
391 | |
392 | private: |
393 | // Two slots per external: Arm and Thumb |
394 | using StubMapEntry = std::tuple<Symbol *, Symbol *>; |
395 | |
396 | Symbol *&getStubSymbolSlot(StringRef Name, bool Thumb) { |
397 | StubMapEntry &Stubs = StubMap.try_emplace(Key: Name).first->second; |
398 | if (Thumb) |
399 | return std::get<1>(t&: Stubs); |
400 | return std::get<0>(t&: Stubs); |
401 | } |
402 | |
403 | DenseMap<StringRef, StubMapEntry> StubMap; |
404 | Section *StubsSection = nullptr; |
405 | }; |
406 | |
407 | } // namespace aarch32 |
408 | } // namespace jitlink |
409 | } // namespace llvm |
410 | |
411 | #endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH32 |
412 | |