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(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