• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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