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 LastDataRelocation = Data_Pointer32,
45
46 ///
47 /// Relocations of class Arm (covers fixed-width 4-byte instruction subset)
48 ///
49 FirstArmRelocation,
50
51 /// Write immediate value for unconditional PC-relative branch with link.
52 /// We patch the instruction opcode to account for an instruction-set state
53 /// switch: we use the bl instruction to stay in ARM and the blx instruction
54 /// to switch to Thumb.
55 Arm_Call = FirstArmRelocation,
56
57 /// Write immediate value for conditional PC-relative branch without link.
58 /// If the branch target is not ARM, we are forced to generate an explicit
59 /// interworking stub.
60 Arm_Jump24,
61
62 /// Write immediate value to the lower halfword of the destination register
63 Arm_MovwAbsNC,
64
65 /// Write immediate value to the top halfword of the destination register
66 Arm_MovtAbs,
67
68 LastArmRelocation = Arm_MovtAbs,
69
70 ///
71 /// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset)
72 ///
73 FirstThumbRelocation,
74
75 /// Write immediate value for unconditional PC-relative branch with link.
76 /// We patch the instruction opcode to account for an instruction-set state
77 /// switch: we use the bl instruction to stay in Thumb and the blx instruction
78 /// to switch to ARM.
79 Thumb_Call = FirstThumbRelocation,
80
81 /// Write immediate value for PC-relative branch without link. The instruction
82 /// can be made conditional by an IT block. If the branch target is not ARM,
83 /// we are forced to generate an explicit interworking stub.
84 Thumb_Jump24,
85
86 /// Write immediate value to the lower halfword of the destination register
87 Thumb_MovwAbsNC,
88
89 /// Write immediate value to the top halfword of the destination register
90 Thumb_MovtAbs,
91
92 /// Write PC-relative immediate value to the lower halfword of the destination
93 /// register
94 Thumb_MovwPrelNC,
95
96 /// Write PC-relative immediate value to the top halfword of the destination
97 /// register
98 Thumb_MovtPrel,
99
100 LastThumbRelocation = Thumb_MovtPrel,
101 LastRelocation = LastThumbRelocation,
102 };
103
104 /// Flags enum for AArch32-specific symbol properties
105 enum TargetFlags_aarch32 : TargetFlagsType {
106 ThumbSymbol = 1 << 0,
107 };
108
109 /// Human-readable name for a given CPU architecture kind
110 const char *getCPUArchName(ARMBuildAttrs::CPUArch K);
111
112 /// Get a human-readable name for the given AArch32 edge kind.
113 const char *getEdgeKindName(Edge::Kind K);
114
115 /// AArch32 uses stubs for a number of purposes, like branch range extension
116 /// or interworking between Arm and Thumb instruction subsets.
117 ///
118 /// Stub implementations vary depending on CPU architecture (v4, v6, v7),
119 /// instruction subset and branch type (absolute/PC-relative).
120 ///
121 /// For each kind of stub, the StubsFlavor defines one concrete form that is
122 /// used throughout the LinkGraph.
123 ///
124 /// Stubs are often called "veneers" in the official docs and online.
125 ///
126 enum StubsFlavor {
127 Unsupported = 0,
128 Thumbv7,
129 };
130
131 /// JITLink sub-arch configuration for Arm CPU models
132 struct ArmConfig {
133 bool J1J2BranchEncoding = false;
134 StubsFlavor Stubs = Unsupported;
135 };
136
137 /// Obtain the sub-arch configuration for a given Arm CPU model.
getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch)138 inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) {
139 ArmConfig ArmCfg;
140 switch (CPUArch) {
141 case ARMBuildAttrs::v7:
142 case ARMBuildAttrs::v8_A:
143 ArmCfg.J1J2BranchEncoding = true;
144 ArmCfg.Stubs = Thumbv7;
145 break;
146 default:
147 DEBUG_WITH_TYPE("jitlink", {
148 dbgs() << " Warning: ARM config not defined for CPU architecture "
149 << getCPUArchName(CPUArch);
150 });
151 break;
152 }
153 return ArmCfg;
154 }
155
156 /// Immutable pair of halfwords, Hi and Lo, with overflow check
157 struct HalfWords {
HalfWordsHalfWords158 constexpr HalfWords() : Hi(0), Lo(0) {}
HalfWordsHalfWords159 constexpr HalfWords(uint32_t Hi, uint32_t Lo) : Hi(Hi), Lo(Lo) {
160 assert(isUInt<16>(Hi) && "Overflow in first half-word");
161 assert(isUInt<16>(Lo) && "Overflow in second half-word");
162 }
163 const uint16_t Hi; // First halfword
164 const uint16_t Lo; // Second halfword
165 };
166
167 /// FixupInfo base class is required for dynamic lookups.
168 struct FixupInfoBase {
169 static const FixupInfoBase *getDynFixupInfo(Edge::Kind K);
~FixupInfoBaseFixupInfoBase170 virtual ~FixupInfoBase() {}
171 };
172
173 /// FixupInfo checks for Arm edge kinds work on 32-bit words
174 struct FixupInfoArm : public FixupInfoBase {
175 bool (*checkOpcode)(uint32_t Wd) = nullptr;
176 };
177
178 /// FixupInfo check for Thumb32 edge kinds work on a pair of 16-bit halfwords
179 struct FixupInfoThumb : public FixupInfoBase {
180 bool (*checkOpcode)(uint16_t Hi, uint16_t Lo) = nullptr;
181 };
182
183 /// Collection of named constants per fixup kind
184 ///
185 /// Mandatory entries:
186 /// Opcode - Values of the op-code bits in the instruction, with
187 /// unaffected bits nulled
188 /// OpcodeMask - Mask with all bits set that encode the op-code
189 ///
190 /// Other common entries:
191 /// ImmMask - Mask with all bits set that encode the immediate value
192 /// RegMask - Mask with all bits set that encode the register
193 ///
194 /// Specializations can add further custom fields without restrictions.
195 ///
196 template <EdgeKind_aarch32 Kind> struct FixupInfo {};
197
198 struct FixupInfoArmBranch : public FixupInfoArm {
199 static constexpr uint32_t Opcode = 0x0a000000;
200 static constexpr uint32_t ImmMask = 0x00ffffff;
201 };
202
203 template <> struct FixupInfo<Arm_Jump24> : public FixupInfoArmBranch {
204 static constexpr uint32_t OpcodeMask = 0x0f000000;
205 };
206
207 template <> struct FixupInfo<Arm_Call> : public FixupInfoArmBranch {
208 static constexpr uint32_t OpcodeMask = 0x0e000000;
209 static constexpr uint32_t CondMask = 0xe0000000; // excluding BLX bit
210 static constexpr uint32_t Unconditional = 0xe0000000;
211 static constexpr uint32_t BitH = 0x01000000;
212 static constexpr uint32_t BitBlx = 0x10000000;
213 };
214
215 struct FixupInfoArmMov : public FixupInfoArm {
216 static constexpr uint32_t OpcodeMask = 0x0ff00000;
217 static constexpr uint32_t ImmMask = 0x000f0fff;
218 static constexpr uint32_t RegMask = 0x0000f000;
219 };
220
221 template <> struct FixupInfo<Arm_MovtAbs> : public FixupInfoArmMov {
222 static constexpr uint32_t Opcode = 0x03400000;
223 };
224
225 template <> struct FixupInfo<Arm_MovwAbsNC> : public FixupInfoArmMov {
226 static constexpr uint32_t Opcode = 0x03000000;
227 };
228
229 template <> struct FixupInfo<Thumb_Jump24> : public FixupInfoThumb {
230 static constexpr HalfWords Opcode{0xf000, 0x9000};
231 static constexpr HalfWords OpcodeMask{0xf800, 0x9000};
232 static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
233 };
234
235 template <> struct FixupInfo<Thumb_Call> : public FixupInfoThumb {
236 static constexpr HalfWords Opcode{0xf000, 0xc000};
237 static constexpr HalfWords OpcodeMask{0xf800, 0xc000};
238 static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
239 static constexpr uint16_t LoBitH = 0x0001;
240 static constexpr uint16_t LoBitNoBlx = 0x1000;
241 };
242
243 struct FixupInfoThumbMov : public FixupInfoThumb {
244 static constexpr HalfWords OpcodeMask{0xfbf0, 0x8000};
245 static constexpr HalfWords ImmMask{0x040f, 0x70ff};
246 static constexpr HalfWords RegMask{0x0000, 0x0f00};
247 };
248
249 template <> struct FixupInfo<Thumb_MovtAbs> : public FixupInfoThumbMov {
250 static constexpr HalfWords Opcode{0xf2c0, 0x0000};
251 };
252
253 template <> struct FixupInfo<Thumb_MovtPrel> : public FixupInfoThumbMov {
254 static constexpr HalfWords Opcode{0xf2c0, 0x0000};
255 };
256
257 template <> struct FixupInfo<Thumb_MovwAbsNC> : public FixupInfoThumbMov {
258 static constexpr HalfWords Opcode{0xf240, 0x0000};
259 };
260
261 template <> struct FixupInfo<Thumb_MovwPrelNC> : public FixupInfoThumbMov {
262 static constexpr HalfWords Opcode{0xf240, 0x0000};
263 };
264
265 /// Helper function to read the initial addend for Data-class relocations.
266 Expected<int64_t> readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset,
267 Edge::Kind Kind);
268
269 /// Helper function to read the initial addend for Arm-class relocations.
270 Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, Edge::OffsetT Offset,
271 Edge::Kind Kind);
272
273 /// Helper function to read the initial addend for Thumb-class relocations.
274 Expected<int64_t> readAddendThumb(LinkGraph &G, Block &B, Edge::OffsetT Offset,
275 Edge::Kind Kind, const ArmConfig &ArmCfg);
276
277 /// Read the initial addend for a REL-type relocation. It's the value encoded
278 /// in the immediate field of the fixup location by the compiler.
279 inline Expected<int64_t> readAddend(LinkGraph &G, Block &B,
280 Edge::OffsetT Offset, Edge::Kind Kind,
281 const ArmConfig &ArmCfg) {
282 if (Kind <= LastDataRelocation)
283 return readAddendData(G, B, Offset, Kind);
284
285 if (Kind <= LastArmRelocation)
286 return readAddendArm(G, B, Offset, Kind);
287
288 if (Kind <= LastThumbRelocation)
289 return readAddendThumb(G, B, Offset, Kind, ArmCfg);
290
291 llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
292 }
293
294 /// Helper function to apply the fixup for Data-class relocations.
295 Error applyFixupData(LinkGraph &G, Block &B, const Edge &E);
296
297 /// Helper function to apply the fixup for Arm-class relocations.
298 Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E);
299
300 /// Helper function to apply the fixup for Thumb-class relocations.
301 Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
302 const ArmConfig &ArmCfg);
303
304 /// Apply fixup expression for edge to block content.
305 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
306 const ArmConfig &ArmCfg) {
307 Edge::Kind Kind = E.getKind();
308
309 if (Kind <= LastDataRelocation)
310 return applyFixupData(G, B, E);
311
312 if (Kind <= LastArmRelocation)
313 return applyFixupArm(G, B, E);
314
315 if (Kind <= LastThumbRelocation)
316 return applyFixupThumb(G, B, E, ArmCfg);
317
318 llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
319 }
320
321 /// Stubs builder for a specific StubsFlavor
322 ///
323 /// Right now we only have one default stub kind, but we want to extend this
324 /// and allow creation of specific kinds in the future (e.g. branch range
325 /// extension or interworking).
326 ///
327 /// Let's keep it simple for the moment and not wire this through a GOT.
328 ///
329 template <StubsFlavor Flavor>
330 class StubsManager : public TableManager<StubsManager<Flavor>> {
331 public:
332 StubsManager() = default;
333
334 /// Name of the object file section that will contain all our stubs.
335 static StringRef getSectionName();
336
337 /// Implements link-graph traversal via visitExistingEdges().
338 bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
339 if (E.getTarget().isDefined())
340 return false;
341
342 switch (E.getKind()) {
343 case Thumb_Call:
344 case Thumb_Jump24: {
345 DEBUG_WITH_TYPE("jitlink", {
346 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
347 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
348 << formatv("{0:x}", E.getOffset()) << ")\n";
349 });
350 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
351 return true;
352 }
353 }
354 return false;
355 }
356
357 /// Create a branch range extension stub for the class's flavor.
358 Symbol &createEntry(LinkGraph &G, Symbol &Target);
359
360 private:
361 /// Create a new node in the link-graph for the given stub template.
362 template <size_t Size>
363 Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size],
364 uint64_t Alignment) {
365 ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
366 return G.createContentBlock(getStubsSection(G), Template,
367 orc::ExecutorAddr(), Alignment, 0);
368 }
369
370 /// Get or create the object file section that will contain all our stubs.
371 Section &getStubsSection(LinkGraph &G) {
372 if (!StubsSection)
373 StubsSection = &G.createSection(getSectionName(),
374 orc::MemProt::Read | orc::MemProt::Exec);
375 return *StubsSection;
376 }
377
378 Section *StubsSection = nullptr;
379 };
380
381 /// Create a branch range extension stub with Thumb encoding for v7 CPUs.
382 template <>
383 Symbol &StubsManager<Thumbv7>::createEntry(LinkGraph &G, Symbol &Target);
384
385 template <> inline StringRef StubsManager<Thumbv7>::getSectionName() {
386 return "__llvm_jitlink_aarch32_STUBS_Thumbv7";
387 }
388
389 } // namespace aarch32
390 } // namespace jitlink
391 } // namespace llvm
392
393 #endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH32
394