1 //===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 specific code ---*- C++ 2 //-*-===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // COFF AArch64 support for MC-JIT runtime dynamic linker. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H 15 #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H 16 17 #include "../RuntimeDyldCOFF.h" 18 #include "llvm/BinaryFormat/COFF.h" 19 #include "llvm/Object/COFF.h" 20 #include "llvm/Support/Endian.h" 21 22 #define DEBUG_TYPE "dyld" 23 24 using namespace llvm::support::endian; 25 26 namespace llvm { 27 28 // This relocation type is used for handling long branch instruction 29 // throught the Stub. 30 enum InternalRelocationType : unsigned { 31 INTERNAL_REL_ARM64_LONG_BRANCH26 = 0x111, 32 }; 33 34 static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); } 35 static void or32le(void *P, int32_t V) { write32le(P, read32le(P) | V); } 36 37 static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) { 38 uint32_t orig = read32le(T); 39 orig &= ~(0xFFF << 10); 40 write32le(T, orig | ((imm & (0xFFF >> rangeLimit)) << 10)); 41 } 42 43 static void write32AArch64Ldr(uint8_t *T, uint64_t imm) { 44 uint32_t orig = read32le(T); 45 uint32_t size = orig >> 30; 46 // 0x04000000 indicates SIMD/FP registers 47 // 0x00800000 indicates 128 bit 48 if ((orig & 0x04800000) == 0x04800000) 49 size += 4; 50 if ((imm & ((1 << size) - 1)) != 0) 51 assert(0 && "misaligned ldr/str offset"); 52 write32AArch64Imm(T, imm >> size, size); 53 } 54 55 static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift) { 56 uint64_t Imm = (s >> shift) - (p >> shift); 57 uint32_t ImmLo = (Imm & 0x3) << 29; 58 uint32_t ImmHi = (Imm & 0x1FFFFC) << 3; 59 uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3); 60 write32le(T, (read32le(T) & ~Mask) | ImmLo | ImmHi); 61 } 62 63 class RuntimeDyldCOFFAArch64 : public RuntimeDyldCOFF { 64 65 private: 66 // When a module is loaded we save the SectionID of the unwind 67 // sections in a table until we receive a request to register all 68 // unregisteredEH frame sections with the memory manager. 69 SmallVector<SID, 2> UnregisteredEHFrameSections; 70 SmallVector<SID, 2> RegisteredEHFrameSections; 71 uint64_t ImageBase; 72 73 // Fake an __ImageBase pointer by returning the section with the lowest adress 74 uint64_t getImageBase() { 75 if (!ImageBase) { 76 ImageBase = std::numeric_limits<uint64_t>::max(); 77 for (const SectionEntry &Section : Sections) 78 // The Sections list may contain sections that weren't loaded for 79 // whatever reason: they may be debug sections, and ProcessAllSections 80 // is false, or they may be sections that contain 0 bytes. If the 81 // section isn't loaded, the load address will be 0, and it should not 82 // be included in the ImageBase calculation. 83 if (Section.getLoadAddress() != 0) 84 ImageBase = std::min(ImageBase, Section.getLoadAddress()); 85 } 86 return ImageBase; 87 } 88 89 public: 90 RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM, 91 JITSymbolResolver &Resolver) 92 : RuntimeDyldCOFF(MM, Resolver), ImageBase(0) {} 93 94 unsigned getStubAlignment() override { return 8; } 95 96 unsigned getMaxStubSize() const override { return 20; } 97 98 std::tuple<uint64_t, uint64_t, uint64_t> 99 generateRelocationStub(unsigned SectionID, StringRef TargetName, 100 uint64_t Offset, uint64_t RelType, uint64_t Addend, 101 StubMap &Stubs) { 102 uintptr_t StubOffset; 103 SectionEntry &Section = Sections[SectionID]; 104 105 RelocationValueRef OriginalRelValueRef; 106 OriginalRelValueRef.SectionID = SectionID; 107 OriginalRelValueRef.Offset = Offset; 108 OriginalRelValueRef.Addend = Addend; 109 OriginalRelValueRef.SymbolName = TargetName.data(); 110 111 auto Stub = Stubs.find(OriginalRelValueRef); 112 if (Stub == Stubs.end()) { 113 LLVM_DEBUG(dbgs() << " Create a new stub function for " 114 << TargetName.data() << "\n"); 115 116 StubOffset = Section.getStubOffset(); 117 Stubs[OriginalRelValueRef] = StubOffset; 118 createStubFunction(Section.getAddressWithOffset(StubOffset)); 119 Section.advanceStubOffset(getMaxStubSize()); 120 } else { 121 LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data() 122 << "\n"); 123 StubOffset = Stub->second; 124 } 125 126 // Resolve original relocation to stub function. 127 const RelocationEntry RE(SectionID, Offset, RelType, Addend); 128 resolveRelocation(RE, Section.getLoadAddressWithOffset(StubOffset)); 129 130 // adjust relocation info so resolution writes to the stub function 131 // Here an internal relocation type is used for resolving long branch via 132 // stub instruction. 133 Addend = 0; 134 Offset = StubOffset; 135 RelType = INTERNAL_REL_ARM64_LONG_BRANCH26; 136 137 return std::make_tuple(Offset, RelType, Addend); 138 } 139 140 Expected<object::relocation_iterator> 141 processRelocationRef(unsigned SectionID, object::relocation_iterator RelI, 142 const object::ObjectFile &Obj, 143 ObjSectionToIDMap &ObjSectionToID, 144 StubMap &Stubs) override { 145 146 auto Symbol = RelI->getSymbol(); 147 if (Symbol == Obj.symbol_end()) 148 report_fatal_error("Unknown symbol in relocation"); 149 150 Expected<StringRef> TargetNameOrErr = Symbol->getName(); 151 if (!TargetNameOrErr) 152 return TargetNameOrErr.takeError(); 153 StringRef TargetName = *TargetNameOrErr; 154 155 auto SectionOrErr = Symbol->getSection(); 156 if (!SectionOrErr) 157 return SectionOrErr.takeError(); 158 auto Section = *SectionOrErr; 159 160 uint64_t RelType = RelI->getType(); 161 uint64_t Offset = RelI->getOffset(); 162 163 // If there is no section, this must be an external reference. 164 const bool IsExtern = Section == Obj.section_end(); 165 166 // Determine the Addend used to adjust the relocation value. 167 uint64_t Addend = 0; 168 SectionEntry &AddendSection = Sections[SectionID]; 169 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset; 170 uint8_t *Displacement = (uint8_t *)ObjTarget; 171 172 switch (RelType) { 173 case COFF::IMAGE_REL_ARM64_ADDR32: 174 case COFF::IMAGE_REL_ARM64_ADDR32NB: 175 case COFF::IMAGE_REL_ARM64_REL32: 176 case COFF::IMAGE_REL_ARM64_SECREL: 177 Addend = read32le(Displacement); 178 break; 179 case COFF::IMAGE_REL_ARM64_BRANCH26: { 180 uint32_t orig = read32le(Displacement); 181 Addend = (orig & 0x03FFFFFF) << 2; 182 183 if (IsExtern) 184 std::tie(Offset, RelType, Addend) = generateRelocationStub( 185 SectionID, TargetName, Offset, RelType, Addend, Stubs); 186 break; 187 } 188 case COFF::IMAGE_REL_ARM64_BRANCH19: { 189 uint32_t orig = read32le(Displacement); 190 Addend = (orig & 0x00FFFFE0) >> 3; 191 break; 192 } 193 case COFF::IMAGE_REL_ARM64_BRANCH14: { 194 uint32_t orig = read32le(Displacement); 195 Addend = (orig & 0x000FFFE0) >> 3; 196 break; 197 } 198 case COFF::IMAGE_REL_ARM64_REL21: 199 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: { 200 uint32_t orig = read32le(Displacement); 201 Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC); 202 break; 203 } 204 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: 205 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: { 206 uint32_t orig = read32le(Displacement); 207 Addend = ((orig >> 10) & 0xFFF); 208 break; 209 } 210 case COFF::IMAGE_REL_ARM64_ADDR64: { 211 Addend = read64le(Displacement); 212 break; 213 } 214 default: 215 break; 216 } 217 218 #if !defined(NDEBUG) 219 SmallString<32> RelTypeName; 220 RelI->getTypeName(RelTypeName); 221 222 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset 223 << " RelType: " << RelTypeName << " TargetName: " 224 << TargetName << " Addend " << Addend << "\n"); 225 #endif 226 227 unsigned TargetSectionID = -1; 228 if (IsExtern) { 229 RelocationEntry RE(SectionID, Offset, RelType, Addend); 230 addRelocationForSymbol(RE, TargetName); 231 } else { 232 if (auto TargetSectionIDOrErr = findOrEmitSection( 233 Obj, *Section, Section->isText(), ObjSectionToID)) { 234 TargetSectionID = *TargetSectionIDOrErr; 235 } else 236 return TargetSectionIDOrErr.takeError(); 237 238 uint64_t TargetOffset = getSymbolOffset(*Symbol); 239 RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend); 240 addRelocationForSection(RE, TargetSectionID); 241 } 242 return ++RelI; 243 } 244 245 void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override { 246 const auto Section = Sections[RE.SectionID]; 247 uint8_t *Target = Section.getAddressWithOffset(RE.Offset); 248 uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset); 249 250 switch (RE.RelType) { 251 default: 252 llvm_unreachable("unsupported relocation type"); 253 case COFF::IMAGE_REL_ARM64_ABSOLUTE: { 254 // This relocation is ignored. 255 break; 256 } 257 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: { 258 // The page base of the target, for ADRP instruction. 259 Value += RE.Addend; 260 write32AArch64Addr(Target, Value, FinalAddress, 12); 261 break; 262 } 263 case COFF::IMAGE_REL_ARM64_REL21: { 264 // The 12-bit relative displacement to the target, for instruction ADR 265 Value += RE.Addend; 266 write32AArch64Addr(Target, Value, FinalAddress, 0); 267 break; 268 } 269 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: { 270 // The 12-bit page offset of the target, 271 // for instructions ADD/ADDS (immediate) with zero shift. 272 Value += RE.Addend; 273 write32AArch64Imm(Target, Value & 0xFFF, 0); 274 break; 275 } 276 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: { 277 // The 12-bit page offset of the target, 278 // for instruction LDR (indexed, unsigned immediate). 279 Value += RE.Addend; 280 write32AArch64Ldr(Target, Value & 0xFFF); 281 break; 282 } 283 case COFF::IMAGE_REL_ARM64_ADDR32: { 284 // The 32-bit VA of the target. 285 uint32_t VA = Value + RE.Addend; 286 write32le(Target, VA); 287 break; 288 } 289 case COFF::IMAGE_REL_ARM64_ADDR32NB: { 290 // The target's 32-bit RVA. 291 uint64_t RVA = Value + RE.Addend - getImageBase(); 292 write32le(Target, RVA); 293 break; 294 } 295 case INTERNAL_REL_ARM64_LONG_BRANCH26: { 296 // Encode the immadiate value for generated Stub instruction (MOVZ) 297 or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5); 298 or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11); 299 or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27); 300 or32le(Target + 0, ((Value + RE.Addend) & 0xFFFF000000000000) >> 43); 301 break; 302 } 303 case COFF::IMAGE_REL_ARM64_BRANCH26: { 304 // The 26-bit relative displacement to the target, for B and BL 305 // instructions. 306 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 307 assert(isInt<28>(PCRelVal) && "Branch target is out of range."); 308 write32le(Target, (read32le(Target) & ~(0x03FFFFFF)) | 309 (PCRelVal & 0x0FFFFFFC) >> 2); 310 break; 311 } 312 case COFF::IMAGE_REL_ARM64_BRANCH19: { 313 // The 19-bit offset to the relocation target, 314 // for conditional B instruction. 315 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 316 assert(isInt<21>(PCRelVal) && "Branch target is out of range."); 317 write32le(Target, (read32le(Target) & ~(0x00FFFFE0)) | 318 (PCRelVal & 0x001FFFFC) << 3); 319 break; 320 } 321 case COFF::IMAGE_REL_ARM64_BRANCH14: { 322 // The 14-bit offset to the relocation target, 323 // for instructions TBZ and TBNZ. 324 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 325 assert(isInt<16>(PCRelVal) && "Branch target is out of range."); 326 write32le(Target, (read32le(Target) & ~(0x000FFFE0)) | 327 (PCRelVal & 0x0000FFFC) << 3); 328 break; 329 } 330 case COFF::IMAGE_REL_ARM64_ADDR64: { 331 // The 64-bit VA of the relocation target. 332 write64le(Target, Value + RE.Addend); 333 break; 334 } 335 case COFF::IMAGE_REL_ARM64_SECTION: { 336 // 16-bit section index of the section that contains the target. 337 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX && 338 "relocation overflow"); 339 add16(Target, RE.SectionID); 340 break; 341 } 342 case COFF::IMAGE_REL_ARM64_SECREL: { 343 // 32-bit offset of the target from the beginning of its section. 344 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX && 345 "Relocation overflow"); 346 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN && 347 "Relocation underflow"); 348 write32le(Target, RE.Addend); 349 break; 350 } 351 case COFF::IMAGE_REL_ARM64_REL32: { 352 // The 32-bit relative address from the byte following the relocation. 353 uint64_t Result = Value - FinalAddress - 4; 354 write32le(Target, Result + RE.Addend); 355 break; 356 } 357 } 358 } 359 360 void registerEHFrames() override {} 361 }; 362 363 } // End namespace llvm 364 365 #endif 366