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.
getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch)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 {
HalfWordsHalfWords165 constexpr HalfWords() : Hi(0), Lo(0) {}
HalfWordsHalfWords166 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);
~FixupInfoBaseFixupInfoBase177 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(Name);
369 return std::make_pair(&Stubs->second, 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(Name).first->second;
398 if (Thumb)
399 return std::get<1>(Stubs);
400 return std::get<0>(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