brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.5 KiB · 3ca6309 Raw
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