123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- //===-- RuntimeDyldCOFFAArch64.h --- COFF/AArch64 specific code ---*- C++
- //-*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // COFF AArch64 support for MC-JIT runtime dynamic linker.
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
- #define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDCOFFAARCH64_H
- #include "../RuntimeDyldCOFF.h"
- #include "llvm/BinaryFormat/COFF.h"
- #include "llvm/Object/COFF.h"
- #include "llvm/Support/Endian.h"
- #define DEBUG_TYPE "dyld"
- using namespace llvm::support::endian;
- namespace llvm {
- // This relocation type is used for handling long branch instruction
- // throught the Stub.
- enum InternalRelocationType : unsigned {
- INTERNAL_REL_ARM64_LONG_BRANCH26 = 0x111,
- };
- static void add16(uint8_t *p, int16_t v) { write16le(p, read16le(p) + v); }
- static void or32le(void *P, int32_t V) { write32le(P, read32le(P) | V); }
- static void write32AArch64Imm(uint8_t *T, uint64_t imm, uint32_t rangeLimit) {
- uint32_t orig = read32le(T);
- orig &= ~(0xFFF << 10);
- write32le(T, orig | ((imm & (0xFFF >> rangeLimit)) << 10));
- }
- static void write32AArch64Ldr(uint8_t *T, uint64_t imm) {
- uint32_t orig = read32le(T);
- uint32_t size = orig >> 30;
- // 0x04000000 indicates SIMD/FP registers
- // 0x00800000 indicates 128 bit
- if ((orig & 0x04800000) == 0x04800000)
- size += 4;
- if ((imm & ((1 << size) - 1)) != 0)
- assert(0 && "misaligned ldr/str offset");
- write32AArch64Imm(T, imm >> size, size);
- }
- static void write32AArch64Addr(void *T, uint64_t s, uint64_t p, int shift) {
- uint64_t Imm = (s >> shift) - (p >> shift);
- uint32_t ImmLo = (Imm & 0x3) << 29;
- uint32_t ImmHi = (Imm & 0x1FFFFC) << 3;
- uint64_t Mask = (0x3 << 29) | (0x1FFFFC << 3);
- write32le(T, (read32le(T) & ~Mask) | ImmLo | ImmHi);
- }
- class RuntimeDyldCOFFAArch64 : public RuntimeDyldCOFF {
- private:
- // When a module is loaded we save the SectionID of the unwind
- // sections in a table until we receive a request to register all
- // unregisteredEH frame sections with the memory manager.
- SmallVector<SID, 2> UnregisteredEHFrameSections;
- SmallVector<SID, 2> RegisteredEHFrameSections;
- uint64_t ImageBase;
- // Fake an __ImageBase pointer by returning the section with the lowest adress
- uint64_t getImageBase() {
- if (!ImageBase) {
- ImageBase = std::numeric_limits<uint64_t>::max();
- for (const SectionEntry &Section : Sections)
- // The Sections list may contain sections that weren't loaded for
- // whatever reason: they may be debug sections, and ProcessAllSections
- // is false, or they may be sections that contain 0 bytes. If the
- // section isn't loaded, the load address will be 0, and it should not
- // be included in the ImageBase calculation.
- if (Section.getLoadAddress() != 0)
- ImageBase = std::min(ImageBase, Section.getLoadAddress());
- }
- return ImageBase;
- }
- public:
- RuntimeDyldCOFFAArch64(RuntimeDyld::MemoryManager &MM,
- JITSymbolResolver &Resolver)
- : RuntimeDyldCOFF(MM, Resolver, 8, COFF::IMAGE_REL_ARM64_ADDR64),
- ImageBase(0) {}
- unsigned getStubAlignment() override { return 8; }
- unsigned getMaxStubSize() const override { return 20; }
- std::tuple<uint64_t, uint64_t, uint64_t>
- generateRelocationStub(unsigned SectionID, StringRef TargetName,
- uint64_t Offset, uint64_t RelType, uint64_t Addend,
- StubMap &Stubs) {
- uintptr_t StubOffset;
- SectionEntry &Section = Sections[SectionID];
- RelocationValueRef OriginalRelValueRef;
- OriginalRelValueRef.SectionID = SectionID;
- OriginalRelValueRef.Offset = Offset;
- OriginalRelValueRef.Addend = Addend;
- OriginalRelValueRef.SymbolName = TargetName.data();
- auto Stub = Stubs.find(OriginalRelValueRef);
- if (Stub == Stubs.end()) {
- LLVM_DEBUG(dbgs() << " Create a new stub function for "
- << TargetName.data() << "\n");
- StubOffset = Section.getStubOffset();
- Stubs[OriginalRelValueRef] = StubOffset;
- createStubFunction(Section.getAddressWithOffset(StubOffset));
- Section.advanceStubOffset(getMaxStubSize());
- } else {
- LLVM_DEBUG(dbgs() << " Stub function found for " << TargetName.data()
- << "\n");
- StubOffset = Stub->second;
- }
- // Resolve original relocation to stub function.
- const RelocationEntry RE(SectionID, Offset, RelType, Addend);
- resolveRelocation(RE, Section.getLoadAddressWithOffset(StubOffset));
- // adjust relocation info so resolution writes to the stub function
- // Here an internal relocation type is used for resolving long branch via
- // stub instruction.
- Addend = 0;
- Offset = StubOffset;
- RelType = INTERNAL_REL_ARM64_LONG_BRANCH26;
- return std::make_tuple(Offset, RelType, Addend);
- }
- Expected<object::relocation_iterator>
- processRelocationRef(unsigned SectionID, object::relocation_iterator RelI,
- const object::ObjectFile &Obj,
- ObjSectionToIDMap &ObjSectionToID,
- StubMap &Stubs) override {
- auto Symbol = RelI->getSymbol();
- if (Symbol == Obj.symbol_end())
- report_fatal_error("Unknown symbol in relocation");
- Expected<StringRef> TargetNameOrErr = Symbol->getName();
- if (!TargetNameOrErr)
- return TargetNameOrErr.takeError();
- StringRef TargetName = *TargetNameOrErr;
- auto SectionOrErr = Symbol->getSection();
- if (!SectionOrErr)
- return SectionOrErr.takeError();
- auto Section = *SectionOrErr;
- uint64_t RelType = RelI->getType();
- uint64_t Offset = RelI->getOffset();
- // If there is no section, this must be an external reference.
- bool IsExtern = Section == Obj.section_end();
- // Determine the Addend used to adjust the relocation value.
- uint64_t Addend = 0;
- SectionEntry &AddendSection = Sections[SectionID];
- uintptr_t ObjTarget = AddendSection.getObjAddress() + Offset;
- uint8_t *Displacement = (uint8_t *)ObjTarget;
- unsigned TargetSectionID = -1;
- uint64_t TargetOffset = -1;
- if (TargetName.startswith(getImportSymbolPrefix())) {
- TargetSectionID = SectionID;
- TargetOffset = getDLLImportOffset(SectionID, Stubs, TargetName);
- TargetName = StringRef();
- IsExtern = false;
- } else if (!IsExtern) {
- if (auto TargetSectionIDOrErr = findOrEmitSection(
- Obj, *Section, Section->isText(), ObjSectionToID))
- TargetSectionID = *TargetSectionIDOrErr;
- else
- return TargetSectionIDOrErr.takeError();
- TargetOffset = getSymbolOffset(*Symbol);
- }
- switch (RelType) {
- case COFF::IMAGE_REL_ARM64_ADDR32:
- case COFF::IMAGE_REL_ARM64_ADDR32NB:
- case COFF::IMAGE_REL_ARM64_REL32:
- case COFF::IMAGE_REL_ARM64_SECREL:
- Addend = read32le(Displacement);
- break;
- case COFF::IMAGE_REL_ARM64_BRANCH26: {
- uint32_t orig = read32le(Displacement);
- Addend = (orig & 0x03FFFFFF) << 2;
- if (IsExtern)
- std::tie(Offset, RelType, Addend) = generateRelocationStub(
- SectionID, TargetName, Offset, RelType, Addend, Stubs);
- break;
- }
- case COFF::IMAGE_REL_ARM64_BRANCH19: {
- uint32_t orig = read32le(Displacement);
- Addend = (orig & 0x00FFFFE0) >> 3;
- break;
- }
- case COFF::IMAGE_REL_ARM64_BRANCH14: {
- uint32_t orig = read32le(Displacement);
- Addend = (orig & 0x000FFFE0) >> 3;
- break;
- }
- case COFF::IMAGE_REL_ARM64_REL21:
- case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {
- uint32_t orig = read32le(Displacement);
- Addend = ((orig >> 29) & 0x3) | ((orig >> 3) & 0x1FFFFC);
- break;
- }
- case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L:
- case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {
- uint32_t orig = read32le(Displacement);
- Addend = ((orig >> 10) & 0xFFF);
- break;
- }
- case COFF::IMAGE_REL_ARM64_ADDR64: {
- Addend = read64le(Displacement);
- break;
- }
- default:
- break;
- }
- #if !defined(NDEBUG)
- SmallString<32> RelTypeName;
- RelI->getTypeName(RelTypeName);
- LLVM_DEBUG(dbgs() << "\t\tIn Section " << SectionID << " Offset " << Offset
- << " RelType: " << RelTypeName << " TargetName: "
- << TargetName << " Addend " << Addend << "\n");
- #endif
- if (IsExtern) {
- RelocationEntry RE(SectionID, Offset, RelType, Addend);
- addRelocationForSymbol(RE, TargetName);
- } else {
- RelocationEntry RE(SectionID, Offset, RelType, TargetOffset + Addend);
- addRelocationForSection(RE, TargetSectionID);
- }
- return ++RelI;
- }
- void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override {
- const auto Section = Sections[RE.SectionID];
- uint8_t *Target = Section.getAddressWithOffset(RE.Offset);
- uint64_t FinalAddress = Section.getLoadAddressWithOffset(RE.Offset);
- switch (RE.RelType) {
- default:
- llvm_unreachable("unsupported relocation type");
- case COFF::IMAGE_REL_ARM64_ABSOLUTE: {
- // This relocation is ignored.
- break;
- }
- case COFF::IMAGE_REL_ARM64_PAGEBASE_REL21: {
- // The page base of the target, for ADRP instruction.
- Value += RE.Addend;
- write32AArch64Addr(Target, Value, FinalAddress, 12);
- break;
- }
- case COFF::IMAGE_REL_ARM64_REL21: {
- // The 12-bit relative displacement to the target, for instruction ADR
- Value += RE.Addend;
- write32AArch64Addr(Target, Value, FinalAddress, 0);
- break;
- }
- case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12A: {
- // The 12-bit page offset of the target,
- // for instructions ADD/ADDS (immediate) with zero shift.
- Value += RE.Addend;
- write32AArch64Imm(Target, Value & 0xFFF, 0);
- break;
- }
- case COFF::IMAGE_REL_ARM64_PAGEOFFSET_12L: {
- // The 12-bit page offset of the target,
- // for instruction LDR (indexed, unsigned immediate).
- Value += RE.Addend;
- write32AArch64Ldr(Target, Value & 0xFFF);
- break;
- }
- case COFF::IMAGE_REL_ARM64_ADDR32: {
- // The 32-bit VA of the target.
- uint32_t VA = Value + RE.Addend;
- write32le(Target, VA);
- break;
- }
- case COFF::IMAGE_REL_ARM64_ADDR32NB: {
- // The target's 32-bit RVA.
- uint64_t RVA = Value + RE.Addend - getImageBase();
- write32le(Target, RVA);
- break;
- }
- case INTERNAL_REL_ARM64_LONG_BRANCH26: {
- // Encode the immadiate value for generated Stub instruction (MOVZ)
- or32le(Target + 12, ((Value + RE.Addend) & 0xFFFF) << 5);
- or32le(Target + 8, ((Value + RE.Addend) & 0xFFFF0000) >> 11);
- or32le(Target + 4, ((Value + RE.Addend) & 0xFFFF00000000) >> 27);
- or32le(Target + 0, ((Value + RE.Addend) & 0xFFFF000000000000) >> 43);
- break;
- }
- case COFF::IMAGE_REL_ARM64_BRANCH26: {
- // The 26-bit relative displacement to the target, for B and BL
- // instructions.
- uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
- assert(isInt<28>(PCRelVal) && "Branch target is out of range.");
- write32le(Target, (read32le(Target) & ~(0x03FFFFFF)) |
- (PCRelVal & 0x0FFFFFFC) >> 2);
- break;
- }
- case COFF::IMAGE_REL_ARM64_BRANCH19: {
- // The 19-bit offset to the relocation target,
- // for conditional B instruction.
- uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
- assert(isInt<21>(PCRelVal) && "Branch target is out of range.");
- write32le(Target, (read32le(Target) & ~(0x00FFFFE0)) |
- (PCRelVal & 0x001FFFFC) << 3);
- break;
- }
- case COFF::IMAGE_REL_ARM64_BRANCH14: {
- // The 14-bit offset to the relocation target,
- // for instructions TBZ and TBNZ.
- uint64_t PCRelVal = Value + RE.Addend - FinalAddress;
- assert(isInt<16>(PCRelVal) && "Branch target is out of range.");
- write32le(Target, (read32le(Target) & ~(0x000FFFE0)) |
- (PCRelVal & 0x0000FFFC) << 3);
- break;
- }
- case COFF::IMAGE_REL_ARM64_ADDR64: {
- // The 64-bit VA of the relocation target.
- write64le(Target, Value + RE.Addend);
- break;
- }
- case COFF::IMAGE_REL_ARM64_SECTION: {
- // 16-bit section index of the section that contains the target.
- assert(static_cast<uint32_t>(RE.SectionID) <= UINT16_MAX &&
- "relocation overflow");
- add16(Target, RE.SectionID);
- break;
- }
- case COFF::IMAGE_REL_ARM64_SECREL: {
- // 32-bit offset of the target from the beginning of its section.
- assert(static_cast<int64_t>(RE.Addend) <= INT32_MAX &&
- "Relocation overflow");
- assert(static_cast<int64_t>(RE.Addend) >= INT32_MIN &&
- "Relocation underflow");
- write32le(Target, RE.Addend);
- break;
- }
- case COFF::IMAGE_REL_ARM64_REL32: {
- // The 32-bit relative address from the byte following the relocation.
- uint64_t Result = Value - FinalAddress - 4;
- write32le(Target, Result + RE.Addend);
- break;
- }
- }
- }
- void registerEHFrames() override {}
- };
- } // End namespace llvm
- #endif
|