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