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