//===- lib/FileFormat/MachO/ArchHandler_arm64.cpp -------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "ArchHandler.h" #include "Atoms.h" #include "MachONormalizedFileBinaryUtils.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" using namespace llvm::MachO; using namespace lld::mach_o::normalized; namespace lld { namespace mach_o { using llvm::support::ulittle32_t; using llvm::support::ulittle64_t; using llvm::support::little32_t; using llvm::support::little64_t; class ArchHandler_arm64 : public ArchHandler { public: ArchHandler_arm64() = default; ~ArchHandler_arm64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::AArch64; } /// Used by GOTPass to locate GOT References bool isGOTAccess(const Reference &ref, bool &canBypassGOT) override { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); switch (ref.kindValue()) { case gotPage21: case gotOffset12: canBypassGOT = true; return true; case delta32ToGOT: case unwindCIEToPersonalityFunction: case imageOffsetGot: canBypassGOT = false; return true; default: return false; } } /// Used by GOTPass to update GOT References. void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { // If GOT slot was instantiated, transform: // gotPage21/gotOffset12 -> page21/offset12scale8 // If GOT slot optimized away, transform: // gotPage21/gotOffset12 -> page21/addOffset12 assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::AArch64); switch (ref->kindValue()) { case gotPage21: const_cast(ref)->setKindValue(page21); break; case gotOffset12: const_cast(ref)->setKindValue(targetNowGOT ? offset12scale8 : addOffset12); break; case delta32ToGOT: const_cast(ref)->setKindValue(delta32); break; case imageOffsetGot: const_cast(ref)->setKindValue(imageOffset); break; default: llvm_unreachable("Not a GOT reference"); } } const StubInfo &stubInfo() override { return _sStubInfo; } bool isCallSite(const Reference &) override; bool isNonCallBranch(const Reference &) override { return false; } bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; bool needsCompactUnwind() override { return true; } Reference::KindValue imageOffsetKind() override { return imageOffset; } Reference::KindValue imageOffsetKindIndirect() override { return imageOffsetGot; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return unwindCIEToPersonalityFunction; } Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } Reference::KindValue unwindRefToFunctionKind() override { return unwindFDEToFunction; } Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } Reference::KindValue pointerKind() override { return pointer64; } Reference::KindValue lazyImmediateLocationKind() override { return lazyImmediateLocation; } uint32_t dwarfCompactUnwindType() override { return 0x03000000; } llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; llvm::Error getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) override; bool needsLocalSymbolInRelocatableFile(const DefinedAtom *atom) override { return (atom->contentType() == DefinedAtom::typeCString); } void generateAtomContent(const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) override; void appendSectionRelocations(const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) override; private: static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfo; enum Arm64Kind : Reference::KindValue { invalid, /// for error condition // Kinds found in mach-o .o files: branch26, /// ex: bl _foo page21, /// ex: adrp x1, _foo@PAGE offset12, /// ex: ldrb w0, [x1, _foo@PAGEOFF] offset12scale2, /// ex: ldrs w0, [x1, _foo@PAGEOFF] offset12scale4, /// ex: ldr w0, [x1, _foo@PAGEOFF] offset12scale8, /// ex: ldr x0, [x1, _foo@PAGEOFF] offset12scale16, /// ex: ldr q0, [x1, _foo@PAGEOFF] gotPage21, /// ex: adrp x1, _foo@GOTPAGE gotOffset12, /// ex: ldr w0, [x1, _foo@GOTPAGEOFF] tlvPage21, /// ex: adrp x1, _foo@TLVPAGE tlvOffset12, /// ex: ldr w0, [x1, _foo@TLVPAGEOFF] pointer64, /// ex: .quad _foo delta64, /// ex: .quad _foo - . delta32, /// ex: .long _foo - . negDelta32, /// ex: .long . - _foo pointer64ToGOT, /// ex: .quad _foo@GOT delta32ToGOT, /// ex: .long _foo@GOT - . // Kinds introduced by Passes: addOffset12, /// Location contains LDR to change into ADD. lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. imageOffset, /// Location contains offset of atom in final image imageOffsetGot, /// Location contains offset of GOT entry for atom in /// final image (typically personality function). unwindCIEToPersonalityFunction, /// Nearly delta32ToGOT, but cannot be /// rematerialized in relocatable object /// (yay for implicit contracts!). unwindFDEToFunction, /// Nearly delta64, but cannot be rematerialized in /// relocatable object (yay for implicit contracts!). unwindInfoToEhFrame, /// Fix low 24 bits of compact unwind encoding to /// refer to __eh_frame entry. }; void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool targetUnnamed); // Utility functions for inspecting/updating instructions. static uint32_t setDisplacementInBranch26(uint32_t instr, int32_t disp); static uint32_t setDisplacementInADRP(uint32_t instr, int64_t disp); static Arm64Kind offset12KindFromInstruction(uint32_t instr); static uint32_t setImm12(uint32_t instr, uint32_t offset); }; const Registry::KindStrings ArchHandler_arm64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch26), LLD_KIND_STRING_ENTRY(page21), LLD_KIND_STRING_ENTRY(offset12), LLD_KIND_STRING_ENTRY(offset12scale2), LLD_KIND_STRING_ENTRY(offset12scale4), LLD_KIND_STRING_ENTRY(offset12scale8), LLD_KIND_STRING_ENTRY(offset12scale16), LLD_KIND_STRING_ENTRY(gotPage21), LLD_KIND_STRING_ENTRY(gotOffset12), LLD_KIND_STRING_ENTRY(tlvPage21), LLD_KIND_STRING_ENTRY(tlvOffset12), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(pointer64ToGOT), LLD_KIND_STRING_ENTRY(delta32ToGOT), LLD_KIND_STRING_ENTRY(addOffset12), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindCIEToPersonalityFunction), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_arm64::_sStubInfo = { "dyld_stub_binder", // Lazy pointer references { Reference::KindArch::AArch64, pointer64, 0, 0 }, { Reference::KindArch::AArch64, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::AArch64, pointer64, 0, 0 }, // arm64 code alignment 2^1 1, // Stub size and code 12, { 0x10, 0x00, 0x00, 0x90, // ADRP X16, lazy_pointer@page 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16, lazy_pointer@pageoff] 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 { Reference::KindArch::AArch64, page21, 0, 0 }, { true, offset12scale8, 4, 0 }, // Stub Helper size and code 12, { 0x50, 0x00, 0x00, 0x18, // LDR W16, L0 0x00, 0x00, 0x00, 0x14, // LDR B helperhelper 0x00, 0x00, 0x00, 0x00 }, // L0: .long 0 { Reference::KindArch::AArch64, lazyImmediateLocation, 8, 0 }, { Reference::KindArch::AArch64, branch26, 4, 0 }, // Stub helper image cache content type DefinedAtom::typeGOT, // Stub Helper-Common size and code 24, // Stub helper alignment 2, { 0x11, 0x00, 0x00, 0x90, // ADRP X17, dyld_ImageLoaderCache@page 0x31, 0x02, 0x00, 0x91, // ADD X17, X17, dyld_ImageLoaderCache@pageoff 0xF0, 0x47, 0xBF, 0xA9, // STP X16/X17, [SP, #-16]! 0x10, 0x00, 0x00, 0x90, // ADRP X16, _fast_lazy_bind@page 0x10, 0x02, 0x40, 0xF9, // LDR X16, [X16,_fast_lazy_bind@pageoff] 0x00, 0x02, 0x1F, 0xD6 }, // BR X16 { Reference::KindArch::AArch64, page21, 0, 0 }, { true, offset12, 4, 0 }, { Reference::KindArch::AArch64, page21, 12, 0 }, { true, offset12scale8, 16, 0 } }; bool ArchHandler_arm64::isCallSite(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); return (ref.kindValue() == branch26); } bool ArchHandler_arm64::isPointer(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::AArch64); Reference::KindValue kind = ref.kindValue(); return (kind == pointer64); } bool ArchHandler_arm64::isPairedReloc(const Relocation &r) { return ((r.type == ARM64_RELOC_ADDEND) || (r.type == ARM64_RELOC_SUBTRACTOR)); } uint32_t ArchHandler_arm64::setDisplacementInBranch26(uint32_t instr, int32_t displacement) { assert((displacement <= 134217727) && (displacement > (-134217728)) && "arm64 branch out of range"); return (instr & 0xFC000000) | ((uint32_t)(displacement >> 2) & 0x03FFFFFF); } uint32_t ArchHandler_arm64::setDisplacementInADRP(uint32_t instruction, int64_t displacement) { assert((displacement <= 0x100000000LL) && (displacement > (-0x100000000LL)) && "arm64 ADRP out of range"); assert(((instruction & 0x9F000000) == 0x90000000) && "reloc not on ADRP instruction"); uint32_t immhi = (displacement >> 9) & (0x00FFFFE0); uint32_t immlo = (displacement << 17) & (0x60000000); return (instruction & 0x9F00001F) | immlo | immhi; } ArchHandler_arm64::Arm64Kind ArchHandler_arm64::offset12KindFromInstruction(uint32_t instruction) { if (instruction & 0x08000000) { switch ((instruction >> 30) & 0x3) { case 0: if ((instruction & 0x04800000) == 0x04800000) return offset12scale16; return offset12; case 1: return offset12scale2; case 2: return offset12scale4; case 3: return offset12scale8; } } return offset12; } uint32_t ArchHandler_arm64::setImm12(uint32_t instruction, uint32_t offset) { assert(((offset & 0xFFFFF000) == 0) && "imm12 offset out of range"); uint32_t imm12 = offset << 10; return (instruction & 0xFFC003FF) | imm12; } llvm::Error ArchHandler_arm64::getReferenceInfo( const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; switch (relocPattern(reloc)) { case ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4: // ex: bl _foo *kind = branch26; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@PAGE *kind = page21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@PAGEOFF] *kind = offset12KindFromInstruction(*(const little32_t *)fixupContent); if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@GOTPAGE *kind = gotPage21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@GOTPAGEOFF] *kind = gotOffset12; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4: // ex: adrp x1, _foo@TLVPAGE *kind = tlvPage21; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4: // ex: ldr x0, [x1, _foo@TLVPAGEOFF] *kind = tlvOffset12; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_UNSIGNED | rExtern | rLength8: // ex: .quad _foo + N *kind = pointer64; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little64_t *)fixupContent; return llvm::Error::success(); case ARM64_RELOC_UNSIGNED | rLength8: // ex: .quad Lfoo + N *kind = pointer64; return atomFromAddress(reloc.symbol, *(const little64_t *)fixupContent, target, addend); case ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8: // ex: .quad _foo@GOT *kind = pointer64ToGOT; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); case ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4: // ex: .long _foo@GOT - . // If we are in an .eh_frame section, then the kind of the relocation should // not be delta32ToGOT. It may instead be unwindCIEToPersonalityFunction. if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindCIEToPersonalityFunction; else *kind = delta32ToGOT; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = 0; return llvm::Error::success(); default: return llvm::make_error("unsupported arm64 relocation type"); } } llvm::Error ArchHandler_arm64::getPairReferenceInfo( const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, bool scatterable, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; switch (relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4): // ex: bl _foo+8 *kind = branch26; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4): // ex: adrp x1, _foo@PAGE *kind = page21; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); case ((ARM64_RELOC_ADDEND | rLength4) << 16 | ARM64_RELOC_PAGEOFF12 | rExtern | rLength4): { // ex: ldr w0, [x1, _foo@PAGEOFF] uint32_t cont32 = (int32_t)*(const little32_t *)fixupContent; *kind = offset12KindFromInstruction(cont32); if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = reloc1.symbol; return llvm::Error::success(); } case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength8): // ex: .quad _foo - . if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; // If we are in an .eh_frame section, then the kind of the relocation should // not be delta64. It may instead be unwindFDEToFunction. if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindFDEToFunction; else *kind = delta64; // The offsets of the 2 relocations must match if (reloc1.offset != reloc2.offset) return llvm::make_error( "paired relocs must have the same offset"); *addend = (int64_t)*(const little64_t *)fixupContent + offsetInAtom; return llvm::Error::success(); case ((ARM64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | ARM64_RELOC_UNSIGNED | rExtern | rLength4): // ex: .quad _foo - . *kind = delta32; if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + offsetInAtom; return llvm::Error::success(); default: return llvm::make_error("unsupported arm64 relocation pair"); } } void ArchHandler_arm64::generateAtomContent( const DefinedAtom &atom, bool relocatable, FindAddressForAtom findAddress, FindAddressForAtom findSectionAddress, uint64_t imageBaseAddress, llvm::MutableArrayRef atomContentBuffer) { // Copy raw bytes. std::copy(atom.rawContent().begin(), atom.rawContent().end(), atomContentBuffer.begin()); // Apply fix-ups. #ifndef NDEBUG if (atom.begin() != atom.end()) { DEBUG_WITH_TYPE("atom-content", llvm::dbgs() << "Applying fixups to atom:\n" << " address=" << llvm::format(" 0x%09lX", &atom) << ", file=#" << atom.file().ordinal() << ", atom=#" << atom.ordinal() << ", name=" << atom.name() << ", type=" << atom.contentType() << "\n"); } #endif for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); bool targetUnnamed = target->name().empty(); uint64_t targetAddress = 0; if (isa(target)) targetAddress = findAddress(*target); uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, targetUnnamed); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, imageBaseAddress, findSectionAddress); } } } void ArchHandler_arm64::applyFixupFinal(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, uint64_t imageBaseAddress, FindAddressForAtom findSectionAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); int32_t displacement; uint32_t instruction; uint32_t value32; uint32_t value64; switch (static_cast(ref.kindValue())) { case branch26: displacement = (targetAddress - fixupAddress) + ref.addend(); *loc32 = setDisplacementInBranch26(*loc32, displacement); return; case page21: case gotPage21: case tlvPage21: displacement = ((targetAddress + ref.addend()) & (-4096)) - (fixupAddress & (-4096)); *loc32 = setDisplacementInADRP(*loc32, displacement); return; case offset12: case gotOffset12: case tlvOffset12: displacement = (targetAddress + ref.addend()) & 0x00000FFF; *loc32 = setImm12(*loc32, displacement); return; case offset12scale2: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x1) == 0) && "scaled imm12 not accessing 2-byte aligneds"); *loc32 = setImm12(*loc32, displacement >> 1); return; case offset12scale4: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x3) == 0) && "scaled imm12 not accessing 4-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 2); return; case offset12scale8: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0x7) == 0) && "scaled imm12 not accessing 8-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 3); return; case offset12scale16: displacement = (targetAddress + ref.addend()) & 0x00000FFF; assert(((displacement & 0xF) == 0) && "scaled imm12 not accessing 16-byte aligned"); *loc32 = setImm12(*loc32, displacement >> 4); return; case addOffset12: instruction = *loc32; assert(((instruction & 0xFFC00000) == 0xF9400000) && "GOT reloc is not an LDR instruction"); displacement = (targetAddress + ref.addend()) & 0x00000FFF; value32 = 0x91000000 | (instruction & 0x000003FF); instruction = setImm12(value32, displacement); *loc32 = instruction; return; case pointer64: case pointer64ToGOT: *loc64 = targetAddress + ref.addend(); return; case delta64: case unwindFDEToFunction: *loc64 = (targetAddress - fixupAddress) + ref.addend(); return; case delta32: case delta32ToGOT: case unwindCIEToPersonalityFunction: *loc32 = (targetAddress - fixupAddress) + ref.addend(); return; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); return; case lazyPointer: // Do nothing return; case lazyImmediateLocation: *loc32 = ref.addend(); return; case imageOffset: *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); return; case imageOffsetGot: llvm_unreachable("imageOffsetGot should have been changed to imageOffset"); break; case unwindInfoToEhFrame: value64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); assert(value64 < 0xffffffU && "offset in __eh_frame too large"); *loc32 = (*loc32 & 0xff000000U) | value64; return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("invalid arm64 Reference Kind"); } void ArchHandler_arm64::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool targetUnnamed) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch26: *loc32 = setDisplacementInBranch26(*loc32, 0); return; case page21: case gotPage21: case tlvPage21: *loc32 = setDisplacementInADRP(*loc32, 0); return; case offset12: case offset12scale2: case offset12scale4: case offset12scale8: case offset12scale16: case gotOffset12: case tlvOffset12: *loc32 = setImm12(*loc32, 0); return; case pointer64: if (targetUnnamed) *loc64 = targetAddress + ref.addend(); else *loc64 = ref.addend(); return; case delta64: *loc64 = ref.addend() + inAtomAddress - fixupAddress; return; case unwindFDEToFunction: // We don't emit unwindFDEToFunction in -r mode as they are implicitly // generated from the data in the __eh_frame section. So here we need // to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc64 = targetAddress - fixupAddress; return; case delta32: *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case negDelta32: // We don't emit negDelta32 in -r mode as they are implicitly // generated from the data in the __eh_frame section. So here we need // to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc32 = fixupAddress - targetAddress + ref.addend(); return; case pointer64ToGOT: *loc64 = 0; return; case delta32ToGOT: *loc32 = inAtomAddress - fixupAddress; return; case unwindCIEToPersonalityFunction: // We don't emit unwindCIEToPersonalityFunction in -r mode as they are // implicitly generated from the data in the __eh_frame section. So here we // need to use the targetAddress so that we can generate the full relocation // when we parse again later. *loc32 = targetAddress - fixupAddress; return; case addOffset12: llvm_unreachable("lazy reference kind implies GOT pass was run"); case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); case imageOffset: case imageOffsetGot: case unwindInfoToEhFrame: llvm_unreachable("fixup implies __unwind_info"); return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown arm64 Reference Kind"); } void ArchHandler_arm64::appendSectionRelocations( const DefinedAtom &atom, uint64_t atomSectionOffset, const Reference &ref, FindSymbolIndexForAtom symbolIndexForAtom, FindSectionIndexForAtom sectionIndexForAtom, FindAddressForAtom addressForAtom, normalized::Relocations &relocs) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::AArch64); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); switch (static_cast(ref.kindValue())) { case branch26: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_BRANCH26 | rPcRel | rExtern | rLength4); } return; case page21: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGE21 | rPcRel | rExtern | rLength4); } return; case offset12: case offset12scale2: case offset12scale4: case offset12scale8: case offset12scale16: if (ref.addend()) { appendReloc(relocs, sectionOffset, ref.addend(), 0, ARM64_RELOC_ADDEND | rLength4); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); } else { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_PAGEOFF12 | rExtern | rLength4); } return; case gotPage21: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_GOT_LOAD_PAGE21 | rPcRel | rExtern | rLength4); return; case gotOffset12: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_GOT_LOAD_PAGEOFF12 | rExtern | rLength4); return; case tlvPage21: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_TLVP_LOAD_PAGE21 | rPcRel | rExtern | rLength4); return; case tlvOffset12: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_TLVP_LOAD_PAGEOFF12 | rExtern | rLength4); return; case pointer64: if (ref.target()->name().empty()) appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rLength8); else appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength8); return; case delta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, ARM64_RELOC_SUBTRACTOR | rExtern | rLength8); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength8); return; case delta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, ARM64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case pointer64ToGOT: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_POINTER_TO_GOT | rExtern | rLength8); return; case delta32ToGOT: assert(ref.addend() == 0); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM64_RELOC_POINTER_TO_GOT | rPcRel | rExtern | rLength4); return; case addOffset12: llvm_unreachable("lazy reference kind implies GOT pass was run"); case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); case imageOffset: case imageOffsetGot: llvm_unreachable("deltas from mach_header can only be in final images"); case unwindCIEToPersonalityFunction: case unwindFDEToFunction: case unwindInfoToEhFrame: case negDelta32: // Do nothing. return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown arm64 Reference Kind"); } std::unique_ptr ArchHandler::create_arm64() { return std::unique_ptr(new ArchHandler_arm64()); } } // namespace mach_o } // namespace lld