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 /// JITLink-internal AArch32 fixup kinds
27 enum EdgeKind_aarch32 : Edge::Kind {
28 
29   ///
30   /// Relocations of class Data respect target endianness (unless otherwise
31   /// specified)
32   ///
33   FirstDataRelocation = Edge::FirstRelocation,
34 
35   /// Relative 32-bit value relocation
36   Data_Delta32 = FirstDataRelocation,
37 
38   /// Absolute 32-bit value relocation
39   Data_Pointer32,
40 
41   LastDataRelocation = Data_Pointer32,
42 
43   ///
44   /// Relocations of class Arm (covers fixed-width 4-byte instruction subset)
45   ///
46   FirstArmRelocation,
47 
48   /// TODO: Arm_Call is here only as a placeholder for now.
49   Arm_Call = FirstArmRelocation,
50 
51   LastArmRelocation = Arm_Call,
52 
53   ///
54   /// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset)
55   ///
56   FirstThumbRelocation,
57 
58   /// Write immediate value for PC-relative branch with link (can bridge between
59   /// Arm and Thumb).
60   Thumb_Call = FirstThumbRelocation,
61 
62   /// Write immediate value for (unconditional) PC-relative branch without link.
63   Thumb_Jump24,
64 
65   /// Write immediate value to the lower halfword of the destination register
66   Thumb_MovwAbsNC,
67 
68   /// Write immediate value to the top halfword of the destination register
69   Thumb_MovtAbs,
70 
71   LastThumbRelocation = Thumb_MovtAbs,
72 };
73 
74 /// Flags enum for AArch32-specific symbol properties
75 enum TargetFlags_aarch32 : TargetFlagsType {
76   ThumbSymbol = 1 << 0,
77 };
78 
79 /// Human-readable name for a given CPU architecture kind
80 const char *getCPUArchName(ARMBuildAttrs::CPUArch K);
81 
82 /// Get a human-readable name for the given AArch32 edge kind.
83 const char *getEdgeKindName(Edge::Kind K);
84 
85 /// AArch32 uses stubs for a number of purposes, like branch range extension
86 /// or interworking between Arm and Thumb instruction subsets.
87 ///
88 /// Stub implementations vary depending on CPU architecture (v4, v6, v7),
89 /// instruction subset and branch type (absolute/PC-relative).
90 ///
91 /// For each kind of stub, the StubsFlavor defines one concrete form that is
92 /// used throughout the LinkGraph.
93 ///
94 /// Stubs are often called "veneers" in the official docs and online.
95 ///
96 enum StubsFlavor {
97   Unsupported = 0,
98   Thumbv7,
99 };
100 
101 /// JITLink sub-arch configuration for Arm CPU models
102 struct ArmConfig {
103   bool J1J2BranchEncoding = false;
104   StubsFlavor Stubs = Unsupported;
105 };
106 
107 /// Obtain the sub-arch configuration for a given Arm CPU model.
108 inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) {
109   ArmConfig ArmCfg;
110   switch (CPUArch) {
111   case ARMBuildAttrs::v7:
112   case ARMBuildAttrs::v8_A:
113     ArmCfg.J1J2BranchEncoding = true;
114     ArmCfg.Stubs = Thumbv7;
115     break;
116   default:
117     DEBUG_WITH_TYPE("jitlink", {
118       dbgs() << "  Warning: ARM config not defined for CPU architecture "
119              << getCPUArchName(CPUArch);
120     });
121     break;
122   }
123   return ArmCfg;
124 }
125 
126 /// Immutable pair of halfwords, Hi and Lo, with overflow check
127 struct HalfWords {
128   constexpr HalfWords() : Hi(0), Lo(0) {}
129   constexpr HalfWords(uint32_t Hi, uint32_t Lo) : Hi(Hi), Lo(Lo) {
130     assert(isUInt<16>(Hi) && "Overflow in first half-word");
131     assert(isUInt<16>(Lo) && "Overflow in second half-word");
132   }
133   const uint16_t Hi; // First halfword
134   const uint16_t Lo; // Second halfword
135 };
136 
137 /// Collection of named constants per fixup kind. It may contain but is not
138 /// limited to the following entries:
139 ///
140 ///   Opcode      - Values of the op-code bits in the instruction, with
141 ///                 unaffected bits nulled
142 ///   OpcodeMask  - Mask with all bits set that encode the op-code
143 ///   ImmMask     - Mask with all bits set that encode the immediate value
144 ///   RegMask     - Mask with all bits set that encode the register
145 ///
146 template <EdgeKind_aarch32 Kind> struct FixupInfo {};
147 
148 template <> struct FixupInfo<Thumb_Jump24> {
149   static constexpr HalfWords Opcode{0xf000, 0x8000};
150   static constexpr HalfWords OpcodeMask{0xf800, 0x8000};
151   static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
152   static constexpr uint16_t LoBitConditional = 0x1000;
153 };
154 
155 template <> struct FixupInfo<Thumb_Call> {
156   static constexpr HalfWords Opcode{0xf000, 0xc000};
157   static constexpr HalfWords OpcodeMask{0xf800, 0xc000};
158   static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
159   static constexpr uint16_t LoBitH = 0x0001;
160   static constexpr uint16_t LoBitNoBlx = 0x1000;
161 };
162 
163 template <> struct FixupInfo<Thumb_MovtAbs> {
164   static constexpr HalfWords Opcode{0xf2c0, 0x0000};
165   static constexpr HalfWords OpcodeMask{0xfbf0, 0x8000};
166   static constexpr HalfWords ImmMask{0x040f, 0x70ff};
167   static constexpr HalfWords RegMask{0x0000, 0x0f00};
168 };
169 
170 template <>
171 struct FixupInfo<Thumb_MovwAbsNC> : public FixupInfo<Thumb_MovtAbs> {
172   static constexpr HalfWords Opcode{0xf240, 0x0000};
173 };
174 
175 /// Helper function to read the initial addend for Data-class relocations.
176 Expected<int64_t> readAddendData(LinkGraph &G, Block &B, const Edge &E);
177 
178 /// Helper function to read the initial addend for Arm-class relocations.
179 Expected<int64_t> readAddendArm(LinkGraph &G, Block &B, const Edge &E);
180 
181 /// Helper function to read the initial addend for Thumb-class relocations.
182 Expected<int64_t> readAddendThumb(LinkGraph &G, Block &B, const Edge &E,
183                                   const ArmConfig &ArmCfg);
184 
185 /// Read the initial addend for a REL-type relocation. It's the value encoded
186 /// in the immediate field of the fixup location by the compiler.
187 inline Expected<int64_t> readAddend(LinkGraph &G, Block &B, const Edge &E,
188                                     const ArmConfig &ArmCfg) {
189   Edge::Kind Kind = E.getKind();
190   if (Kind <= LastDataRelocation)
191     return readAddendData(G, B, E);
192 
193   if (Kind <= LastArmRelocation)
194     return readAddendArm(G, B, E);
195 
196   if (Kind <= LastThumbRelocation)
197     return readAddendThumb(G, B, E, ArmCfg);
198 
199   llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
200 }
201 
202 /// Helper function to apply the fixup for Data-class relocations.
203 Error applyFixupData(LinkGraph &G, Block &B, const Edge &E);
204 
205 /// Helper function to apply the fixup for Arm-class relocations.
206 Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E);
207 
208 /// Helper function to apply the fixup for Thumb-class relocations.
209 Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
210                       const ArmConfig &ArmCfg);
211 
212 /// Apply fixup expression for edge to block content.
213 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
214                         const ArmConfig &ArmCfg) {
215   Edge::Kind Kind = E.getKind();
216 
217   if (Kind <= LastDataRelocation)
218     return applyFixupData(G, B, E);
219 
220   if (Kind <= LastArmRelocation)
221     return applyFixupArm(G, B, E);
222 
223   if (Kind <= LastThumbRelocation)
224     return applyFixupThumb(G, B, E, ArmCfg);
225 
226   llvm_unreachable("Relocation must be of class Data, Arm or Thumb");
227 }
228 
229 /// Stubs builder for a specific StubsFlavor
230 ///
231 /// Right now we only have one default stub kind, but we want to extend this
232 /// and allow creation of specific kinds in the future (e.g. branch range
233 /// extension or interworking).
234 ///
235 /// Let's keep it simple for the moment and not wire this through a GOT.
236 ///
237 template <StubsFlavor Flavor>
238 class StubsManager : public TableManager<StubsManager<Flavor>> {
239 public:
240   StubsManager() = default;
241 
242   /// Name of the object file section that will contain all our stubs.
243   static StringRef getSectionName() { return "__llvm_jitlink_STUBS"; }
244 
245   /// Implements link-graph traversal via visitExistingEdges().
246   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
247     if (E.getTarget().isDefined())
248       return false;
249 
250     switch (E.getKind()) {
251     case Thumb_Call:
252     case Thumb_Jump24: {
253       DEBUG_WITH_TYPE("jitlink", {
254         dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
255                << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
256                << formatv("{0:x}", E.getOffset()) << ")\n";
257       });
258       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
259       return true;
260     }
261     }
262     return false;
263   }
264 
265   /// Create a branch range extension stub for the class's flavor.
266   Symbol &createEntry(LinkGraph &G, Symbol &Target);
267 
268 private:
269   /// Create a new node in the link-graph for the given stub template.
270   template <size_t Size>
271   Block &addStub(LinkGraph &G, const uint8_t (&Code)[Size],
272                  uint64_t Alignment) {
273     ArrayRef<char> Template(reinterpret_cast<const char *>(Code), Size);
274     return G.createContentBlock(getStubsSection(G), Template,
275                                 orc::ExecutorAddr(), Alignment, 0);
276   }
277 
278   /// Get or create the object file section that will contain all our stubs.
279   Section &getStubsSection(LinkGraph &G) {
280     if (!StubsSection)
281       StubsSection = &G.createSection(getSectionName(),
282                                       orc::MemProt::Read | orc::MemProt::Exec);
283     return *StubsSection;
284   }
285 
286   Section *StubsSection = nullptr;
287 };
288 
289 /// Create a branch range extension stub with Thumb encoding for v7 CPUs.
290 template <>
291 Symbol &StubsManager<Thumbv7>::createEntry(LinkGraph &G, Symbol &Target);
292 
293 } // namespace aarch32
294 } // namespace jitlink
295 } // namespace llvm
296 
297 #endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH32
298