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, 8, COFF::IMAGE_REL_ARM64_ADDR64), 93 ImageBase(0) {} 94 95 unsigned getStubAlignment() override { return 8; } 96 97 unsigned getMaxStubSize() const override { return 20; } 98 99 std::tuple<uint64_t, uint64_t, uint64_t> 100 generateRelocationStub(unsigned SectionID, StringRef TargetName, 101 uint64_t Offset, uint64_t RelType, uint64_t Addend, 102 StubMap &Stubs) { 103 uintptr_t StubOffset; 104 SectionEntry &Section = Sections[SectionID]; 105 106 RelocationValueRef OriginalRelValueRef; 107 OriginalRelValueRef.SectionID = SectionID; 108 OriginalRelValueRef.Offset = Offset; 109 OriginalRelValueRef.Addend = Addend; 110 OriginalRelValueRef.SymbolName = TargetName.data(); 111 112 auto Stub = Stubs.find(OriginalRelValueRef); 113 if (Stub == Stubs.end()) { 114 LLVM_DEBUG(dbgs() << " Create a new stub function for " 115 << TargetName.data() << "\n"); 116 117 StubOffset = Section.getStubOffset(); 118 Stubs[OriginalRelValueRef] = StubOffset; 119 createStubFunction(Section.getAddressWithOffset(StubOffset)); 120 Section.advanceStubOffset(getMaxStubSize()); 121 } else { 122 LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data() 123 << "\n"); 124 StubOffset = Stub->second; 125 } 126 127 // Resolve original relocation to stub function. 128 const RelocationEntry RE(SectionID, Offset, RelType, Addend); 129 resolveRelocation(RE, Section.getLoadAddressWithOffset(StubOffset)); 130 131 // adjust relocation info so resolution writes to the stub function 132 // Here an internal relocation type is used for resolving long branch via 133 // stub instruction. 134 Addend = 0; 135 Offset = StubOffset; 136 RelType = INTERNAL_REL_ARM64_LONG_BRANCH26; 137 138 return std::make_tuple(Offset, RelType, Addend); 139 } 140 141 Expected<object::relocation_iterator> 142 processRelocationRef(unsigned SectionID, object::relocation_iterator RelI, 143 const object::ObjectFile &Obj, 144 ObjSectionToIDMap &ObjSectionToID, 145 StubMap &Stubs) override { 146 147 auto Symbol = RelI->getSymbol(); 148 if (Symbol == Obj.symbol_end()) 149 report_fatal_error("Unknown symbol in relocation"); 150 151 Expected<StringRef> TargetNameOrErr = Symbol->getName(); 152 if (!TargetNameOrErr) 153 return TargetNameOrErr.takeError(); 154 StringRef TargetName = *TargetNameOrErr; 155 156 auto SectionOrErr = Symbol->getSection(); 157 if (!SectionOrErr) 158 return SectionOrErr.takeError(); 159 auto Section = *SectionOrErr; 160 161 uint64_t RelType = RelI->getType(); 162 uint64_t Offset = RelI->getOffset(); 163 164 // If there is no section, this must be an external reference. 165 bool IsExtern = Section == Obj.section_end(); 166 167 // Determine the Addend used to adjust the relocation value. 168 uint64_t Addend = 0; 169 SectionEntry &AddendSection = Sections[SectionID]; 170 uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset; 171 uint8_t *Displacement = (uint8_t *)ObjTarget; 172 173 unsigned TargetSectionID = -1; 174 uint64_t TargetOffset = -1; 175 176 if (TargetName.startswith(getImportSymbolPrefix())) { 177 TargetSectionID = SectionID; 178 TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName); 179 TargetName = StringRef(); 180 IsExtern = false; 181 } else if (!IsExtern) { 182 if (auto TargetSectionIDOrErr = findOrEmitSection( 183 Obj, *Section, Section->isText(), ObjSectionToID)) 184 TargetSectionID = *TargetSectionIDOrErr; 185 else 186 return TargetSectionIDOrErr.takeError(); 187 188 TargetOffset = getSymbolOffset(*Symbol); 189 } 190 191 switch (RelType) { 192 case COFF::IMAGE_REL_ARM64_ADDR32: 193 case COFF::IMAGE_REL_ARM64_ADDR32NB: 194 case COFF::IMAGE_REL_ARM64_REL32: 195 case COFF::IMAGE_REL_ARM64_SECREL: 196 Addend = read32le(Displacement); 197 break; 198 case COFF::IMAGE_REL_ARM64_BRANCH26: { 199 uint32_t orig = read32le(Displacement); 200 Addend = (orig & 0x03FFFFFF) << 2; 201 202 if (IsExtern) 203 std::tie(Offset, RelType, Addend) = generateRelocationStub( 204 SectionID, TargetName, Offset, RelType, Addend, Stubs); 205 break; 206 } 207 case COFF::IMAGE_REL_ARM64_BRANCH19: { 208 uint32_t orig = read32le(Displacement); 209 Addend = (orig & 0x00FFFFE0) >> 3; 210 break; 211 } 212 case COFF::IMAGE_REL_ARM64_BRANCH14: { 213 uint32_t orig = read32le(Displacement); 214 Addend = (orig & 0x000FFFE0) >> 3; 215 break; 216 } 217 case COFF::IMAGE_REL_ARM64_REL21: 218 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: { 219 uint32_t orig = read32le(Displacement); 220 Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC); 221 break; 222 } 223 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: 224 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: { 225 uint32_t orig = read32le(Displacement); 226 Addend = ((orig >> 10) & 0xFFF); 227 break; 228 } 229 case COFF::IMAGE_REL_ARM64_ADDR64: { 230 Addend = read64le(Displacement); 231 break; 232 } 233 default: 234 break; 235 } 236 237 #if !defined(NDEBUG) 238 SmallString<32> RelTypeName; 239 RelI->getTypeName(RelTypeName); 240 241 LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset 242 << " RelType: " << RelTypeName << " TargetName: " 243 << TargetName << " Addend " << Addend << "\n"); 244 #endif 245 246 if (IsExtern) { 247 RelocationEntry RE(SectionID, Offset, RelType, Addend); 248 addRelocationForSymbol(RE, TargetName); 249 } else { 250 RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend); 251 addRelocationForSection(RE, TargetSectionID); 252 } 253 return ++RelI; 254 } 255 256 void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override { 257 const auto Section = Sections[RE.SectionID]; 258 uint8_t *Target = Section.getAddressWithOffset(RE.Offset); 259 uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset); 260 261 switch (RE.RelType) { 262 default: 263 llvm_unreachable("unsupported relocation type"); 264 case COFF::IMAGE_REL_ARM64_ABSOLUTE: { 265 // This relocation is ignored. 266 break; 267 } 268 case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: { 269 // The page base of the target, for ADRP instruction. 270 Value += RE.Addend; 271 write32AArch64Addr(Target, Value, FinalAddress, 12); 272 break; 273 } 274 case COFF::IMAGE_REL_ARM64_REL21: { 275 // The 12-bit relative displacement to the target, for instruction ADR 276 Value += RE.Addend; 277 write32AArch64Addr(Target, Value, FinalAddress, 0); 278 break; 279 } 280 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: { 281 // The 12-bit page offset of the target, 282 // for instructions ADD/ADDS (immediate) with zero shift. 283 Value += RE.Addend; 284 write32AArch64Imm(Target, Value & 0xFFF, 0); 285 break; 286 } 287 case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: { 288 // The 12-bit page offset of the target, 289 // for instruction LDR (indexed, unsigned immediate). 290 Value += RE.Addend; 291 write32AArch64Ldr(Target, Value & 0xFFF); 292 break; 293 } 294 case COFF::IMAGE_REL_ARM64_ADDR32: { 295 // The 32-bit VA of the target. 296 uint32_t VA = Value + RE.Addend; 297 write32le(Target, VA); 298 break; 299 } 300 case COFF::IMAGE_REL_ARM64_ADDR32NB: { 301 // The target's 32-bit RVA. 302 uint64_t RVA = Value + RE.Addend - getImageBase(); 303 write32le(Target, RVA); 304 break; 305 } 306 case INTERNAL_REL_ARM64_LONG_BRANCH26: { 307 // Encode the immadiate value for generated Stub instruction (MOVZ) 308 or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5); 309 or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11); 310 or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27); 311 or32le(Target + 0, ((Value + RE.Addend) & 0xFFFF000000000000) >> 43); 312 break; 313 } 314 case COFF::IMAGE_REL_ARM64_BRANCH26: { 315 // The 26-bit relative displacement to the target, for B and BL 316 // instructions. 317 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 318 assert(isInt<28>(PCRelVal) && "Branch target is out of range."); 319 write32le(Target, (read32le(Target) & ~(0x03FFFFFF)) | 320 (PCRelVal & 0x0FFFFFFC) >> 2); 321 break; 322 } 323 case COFF::IMAGE_REL_ARM64_BRANCH19: { 324 // The 19-bit offset to the relocation target, 325 // for conditional B instruction. 326 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 327 assert(isInt<21>(PCRelVal) && "Branch target is out of range."); 328 write32le(Target, (read32le(Target) & ~(0x00FFFFE0)) | 329 (PCRelVal & 0x001FFFFC) << 3); 330 break; 331 } 332 case COFF::IMAGE_REL_ARM64_BRANCH14: { 333 // The 14-bit offset to the relocation target, 334 // for instructions TBZ and TBNZ. 335 uint64_t PCRelVal = Value + RE.Addend - FinalAddress; 336 assert(isInt<16>(PCRelVal) && "Branch target is out of range."); 337 write32le(Target, (read32le(Target) & ~(0x000FFFE0)) | 338 (PCRelVal & 0x0000FFFC) << 3); 339 break; 340 } 341 case COFF::IMAGE_REL_ARM64_ADDR64: { 342 // The 64-bit VA of the relocation target. 343 write64le(Target, Value + RE.Addend); 344 break; 345 } 346 case COFF::IMAGE_REL_ARM64_SECTION: { 347 // 16-bit section index of the section that contains the target. 348 assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX && 349 "relocation overflow"); 350 add16(Target, RE.SectionID); 351 break; 352 } 353 case COFF::IMAGE_REL_ARM64_SECREL: { 354 // 32-bit offset of the target from the beginning of its section. 355 assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX && 356 "Relocation overflow"); 357 assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN && 358 "Relocation underflow"); 359 write32le(Target, RE.Addend); 360 break; 361 } 362 case COFF::IMAGE_REL_ARM64_REL32: { 363 // The 32-bit relative address from the byte following the relocation. 364 uint64_t Result = Value - FinalAddress - 4; 365 write32le(Target, Result + RE.Addend); 366 break; 367 } 368 } 369 } 370 371 void registerEHFrames() override {} 372 }; 373 374 } // End namespace llvm 375 376 #endif 377