Path: blob/main/contrib/llvm-project/llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldCOFFAArch64.h
35294 views
//===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 specific code ---*- C++1//-*-===//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//===----------------------------------------------------------------------===//1213#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H14#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H1516#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"2122#define DEBUG_TYPE "dyld"2324using namespace llvm::support::endian;2526namespace llvm {2728// This relocation type is used for handling long branch instruction29// through the Stub.30enum InternalRelocationType : unsigned {31INTERNAL_REL_ARM64_LONG_BRANCH26 = 0x111,32};3334static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }35static void or32le(void *P, int32_t V) { write32le(P, read32le(P) | V); }3637static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) {38uint32_t orig = read32le(T);39orig &= ~(0xFFF << 10);40write32le(T, orig | ((imm & (0xFFF >> rangeLimit)) << 10));41}4243static void write32AArch64Ldr(uint8_t *T, uint64_t imm) {44uint32_t orig = read32le(T);45uint32_t size = orig >> 30;46// 0x04000000 indicates SIMD/FP registers47// 0x00800000 indicates 128 bit48if ((orig & 0x04800000) == 0x04800000)49size += 4;50if ((imm & ((1 << size) - 1)) != 0)51assert(0 && "misaligned ldr/str offset");52write32AArch64Imm(T, imm >> size, size);53}5455static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift) {56uint64_t Imm = (s >> shift) - (p >> shift);57uint32_t ImmLo = (Imm & 0x3) << 29;58uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;59uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);60write32le(T, (read32le(T) & ~Mask) | ImmLo | ImmHi);61}6263class RuntimeDyldCOFFAArch64 : public RuntimeDyldCOFF {6465private:66// When a module is loaded we save the SectionID of the unwind67// sections in a table until we receive a request to register all68// unregisteredEH frame sections with the memory manager.69SmallVector<SID, 2> UnregisteredEHFrameSections;70SmallVector<SID, 2> RegisteredEHFrameSections;71uint64_t ImageBase;7273// Fake an __ImageBase pointer by returning the section with the lowest adress74uint64_t getImageBase() {75if (!ImageBase) {76ImageBase = std::numeric_limits<uint64_t>::max();77for (const SectionEntry &Section : Sections)78// The Sections list may contain sections that weren't loaded for79// whatever reason: they may be debug sections, and ProcessAllSections80// is false, or they may be sections that contain 0 bytes. If the81// section isn't loaded, the load address will be 0, and it should not82// be included in the ImageBase calculation.83if (Section.getLoadAddress() != 0)84ImageBase = std::min(ImageBase, Section.getLoadAddress());85}86return ImageBase;87}8889public:90RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM,91JITSymbolResolver &Resolver)92: RuntimeDyldCOFF(MM, Resolver, 8, COFF::IMAGE_REL_ARM64_ADDR64),93ImageBase(0) {}9495Align getStubAlignment() override { return Align(8); }9697unsigned getMaxStubSize() const override { return 20; }9899std::tuple<uint64_t, uint64_t, uint64_t>100generateRelocationStub(unsigned SectionID, StringRef TargetName,101uint64_t Offset, uint64_t RelType, uint64_t Addend,102StubMap &Stubs) {103uintptr_t StubOffset;104SectionEntry &Section = Sections[SectionID];105106RelocationValueRef OriginalRelValueRef;107OriginalRelValueRef.SectionID = SectionID;108OriginalRelValueRef.Offset = Offset;109OriginalRelValueRef.Addend = Addend;110OriginalRelValueRef.SymbolName = TargetName.data();111112auto Stub = Stubs.find(OriginalRelValueRef);113if (Stub == Stubs.end()) {114LLVM_DEBUG(dbgs() << " Create a new stub function for "115<< TargetName.data() << "\n");116117StubOffset = Section.getStubOffset();118Stubs[OriginalRelValueRef] = StubOffset;119createStubFunction(Section.getAddressWithOffset(StubOffset));120Section.advanceStubOffset(getMaxStubSize());121} else {122LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data()123<< "\n");124StubOffset = Stub->second;125}126127// Resolve original relocation to stub function.128const RelocationEntry RE(SectionID, Offset, RelType, Addend);129resolveRelocation(RE, Section.getLoadAddressWithOffset(StubOffset));130131// adjust relocation info so resolution writes to the stub function132// Here an internal relocation type is used for resolving long branch via133// stub instruction.134Addend = 0;135Offset = StubOffset;136RelType = INTERNAL_REL_ARM64_LONG_BRANCH26;137138return std::make_tuple(Offset, RelType, Addend);139}140141Expected<object::relocation_iterator>142processRelocationRef(unsigned SectionID, object::relocation_iterator RelI,143const object::ObjectFile &Obj,144ObjSectionToIDMap &ObjSectionToID,145StubMap &Stubs) override {146147auto Symbol = RelI->getSymbol();148if (Symbol == Obj.symbol_end())149report_fatal_error("Unknown symbol in relocation");150151Expected<StringRef> TargetNameOrErr = Symbol->getName();152if (!TargetNameOrErr)153return TargetNameOrErr.takeError();154StringRef TargetName = *TargetNameOrErr;155156auto SectionOrErr = Symbol->getSection();157if (!SectionOrErr)158return SectionOrErr.takeError();159auto Section = *SectionOrErr;160161uint64_t RelType = RelI->getType();162uint64_t Offset = RelI->getOffset();163164// If there is no section, this must be an external reference.165bool IsExtern = Section == Obj.section_end();166167// Determine the Addend used to adjust the relocation value.168uint64_t Addend = 0;169SectionEntry &AddendSection = Sections[SectionID];170uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;171uint8_t *Displacement = (uint8_t *)ObjTarget;172173unsigned TargetSectionID = -1;174uint64_t TargetOffset = -1;175176if (TargetName.starts_with(getImportSymbolPrefix())) {177TargetSectionID = SectionID;178TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName);179TargetName = StringRef();180IsExtern = false;181} else if (!IsExtern) {182if (auto TargetSectionIDOrErr = findOrEmitSection(183Obj, *Section, Section->isText(), ObjSectionToID))184TargetSectionID = *TargetSectionIDOrErr;185else186return TargetSectionIDOrErr.takeError();187188TargetOffset = getSymbolOffset(*Symbol);189}190191switch (RelType) {192case COFF::IMAGE_REL_ARM64_ADDR32:193case COFF::IMAGE_REL_ARM64_ADDR32NB:194case COFF::IMAGE_REL_ARM64_REL32:195case COFF::IMAGE_REL_ARM64_SECREL:196Addend = read32le(Displacement);197break;198case COFF::IMAGE_REL_ARM64_BRANCH26: {199uint32_t orig = read32le(Displacement);200Addend = (orig & 0x03FFFFFF) << 2;201202if (IsExtern)203std::tie(Offset, RelType, Addend) = generateRelocationStub(204SectionID, TargetName, Offset, RelType, Addend, Stubs);205break;206}207case COFF::IMAGE_REL_ARM64_BRANCH19: {208uint32_t orig = read32le(Displacement);209Addend = (orig & 0x00FFFFE0) >> 3;210break;211}212case COFF::IMAGE_REL_ARM64_BRANCH14: {213uint32_t orig = read32le(Displacement);214Addend = (orig & 0x000FFFE0) >> 3;215break;216}217case COFF::IMAGE_REL_ARM64_REL21:218case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {219uint32_t orig = read32le(Displacement);220Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);221break;222}223case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L:224case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {225uint32_t orig = read32le(Displacement);226Addend = ((orig >> 10) & 0xFFF);227break;228}229case COFF::IMAGE_REL_ARM64_ADDR64: {230Addend = read64le(Displacement);231break;232}233default:234break;235}236237#if !defined(NDEBUG)238SmallString<32> RelTypeName;239RelI->getTypeName(RelTypeName);240241LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset242<< " RelType: " << RelTypeName << " TargetName: "243<< TargetName << " Addend " << Addend << "\n");244#endif245246if (IsExtern) {247RelocationEntry RE(SectionID, Offset, RelType, Addend);248addRelocationForSymbol(RE, TargetName);249} else {250RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);251addRelocationForSection(RE, TargetSectionID);252}253return ++RelI;254}255256void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {257const auto Section = Sections[RE.SectionID];258uint8_t *Target = Section.getAddressWithOffset(RE.Offset);259uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);260261switch (RE.RelType) {262default:263llvm_unreachable("unsupported relocation type");264case COFF::IMAGE_REL_ARM64_ABSOLUTE: {265// This relocation is ignored.266break;267}268case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {269// The page base of the target, for ADRP instruction.270Value += RE.Addend;271write32AArch64Addr(Target, Value, FinalAddress, 12);272break;273}274case COFF::IMAGE_REL_ARM64_REL21: {275// The 12-bit relative displacement to the target, for instruction ADR276Value += RE.Addend;277write32AArch64Addr(Target, Value, FinalAddress, 0);278break;279}280case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {281// The 12-bit page offset of the target,282// for instructions ADD/ADDS (immediate) with zero shift.283Value += RE.Addend;284write32AArch64Imm(Target, Value & 0xFFF, 0);285break;286}287case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: {288// The 12-bit page offset of the target,289// for instruction LDR (indexed, unsigned immediate).290Value += RE.Addend;291write32AArch64Ldr(Target, Value & 0xFFF);292break;293}294case COFF::IMAGE_REL_ARM64_ADDR32: {295// The 32-bit VA of the target.296uint32_t VA = Value + RE.Addend;297write32le(Target, VA);298break;299}300case COFF::IMAGE_REL_ARM64_ADDR32NB: {301// The target's 32-bit RVA.302uint64_t RVA = Value + RE.Addend - getImageBase();303write32le(Target, RVA);304break;305}306case INTERNAL_REL_ARM64_LONG_BRANCH26: {307// Encode the immadiate value for generated Stub instruction (MOVZ)308or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5);309or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11);310or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27);311or32le(Target + 0, ((Value + RE.Addend) & 0xFFFF000000000000) >> 43);312break;313}314case COFF::IMAGE_REL_ARM64_BRANCH26: {315// The 26-bit relative displacement to the target, for B and BL316// instructions.317uint64_t PCRelVal = Value + RE.Addend - FinalAddress;318assert(isInt<28>(PCRelVal) && "Branch target is out of range.");319write32le(Target, (read32le(Target) & ~(0x03FFFFFF)) |320(PCRelVal & 0x0FFFFFFC) >> 2);321break;322}323case COFF::IMAGE_REL_ARM64_BRANCH19: {324// The 19-bit offset to the relocation target,325// for conditional B instruction.326uint64_t PCRelVal = Value + RE.Addend - FinalAddress;327assert(isInt<21>(PCRelVal) && "Branch target is out of range.");328write32le(Target, (read32le(Target) & ~(0x00FFFFE0)) |329(PCRelVal & 0x001FFFFC) << 3);330break;331}332case COFF::IMAGE_REL_ARM64_BRANCH14: {333// The 14-bit offset to the relocation target,334// for instructions TBZ and TBNZ.335uint64_t PCRelVal = Value + RE.Addend - FinalAddress;336assert(isInt<16>(PCRelVal) && "Branch target is out of range.");337write32le(Target, (read32le(Target) & ~(0x000FFFE0)) |338(PCRelVal & 0x0000FFFC) << 3);339break;340}341case COFF::IMAGE_REL_ARM64_ADDR64: {342// The 64-bit VA of the relocation target.343write64le(Target, Value + RE.Addend);344break;345}346case COFF::IMAGE_REL_ARM64_SECTION: {347// 16-bit section index of the section that contains the target.348assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&349"relocation overflow");350add16(Target, RE.SectionID);351break;352}353case COFF::IMAGE_REL_ARM64_SECREL: {354// 32-bit offset of the target from the beginning of its section.355assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&356"Relocation overflow");357assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&358"Relocation underflow");359write32le(Target, RE.Addend);360break;361}362case COFF::IMAGE_REL_ARM64_REL32: {363// The 32-bit relative address from the byte following the relocation.364uint64_t Result = Value - FinalAddress - 4;365write32le(Target, Result + RE.Addend);366break;367}368}369}370371void registerEHFrames() override {}372};373374} // End namespace llvm375376#endif377378379