//===- lib/FileFormat/MachO/ArchHandler_x86_64.cpp ------------------------===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #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" 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_x86_64 : public ArchHandler { public: ArchHandler_x86_64() = default; ~ArchHandler_x86_64() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::x86_64; } /// 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::x86_64); switch (ref.kindValue()) { case ripRel32GotLoad: canBypassGOT = true; return true; case ripRel32Got: canBypassGOT = false; return true; case imageOffsetGot: canBypassGOT = false; return true; default: return false; } } bool isTLVAccess(const Reference &ref) const override { assert(ref.kindNamespace() == Reference::KindNamespace::mach_o); assert(ref.kindArch() == Reference::KindArch::x86_64); return ref.kindValue() == ripRel32Tlv; } void updateReferenceToTLV(const Reference *ref) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::x86_64); assert(ref->kindValue() == ripRel32Tlv); const_cast(ref)->setKindValue(ripRel32); } /// Used by GOTPass to update GOT References void updateReferenceToGOT(const Reference *ref, bool targetNowGOT) override { assert(ref->kindNamespace() == Reference::KindNamespace::mach_o); assert(ref->kindArch() == Reference::KindArch::x86_64); switch (ref->kindValue()) { case ripRel32Got: assert(targetNowGOT && "target must be GOT"); case ripRel32GotLoad: const_cast(ref) ->setKindValue(targetNowGOT ? ripRel32 : ripRel32GotLoadNowLea); break; case imageOffsetGot: const_cast(ref)->setKindValue(imageOffset); break; default: llvm_unreachable("unknown GOT reference kind"); } } bool needsCompactUnwind() override { return true; } Reference::KindValue imageOffsetKind() override { return imageOffset; } Reference::KindValue imageOffsetKindIndirect() override { return imageOffsetGot; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return ripRel32Got; } Reference::KindValue unwindRefToCIEKind() override { return negDelta32; } Reference::KindValue unwindRefToFunctionKind() override{ return unwindFDEToFunction; } Reference::KindValue lazyImmediateLocationKind() override { return lazyImmediateLocation; } Reference::KindValue unwindRefToEhFrameKind() override { return unwindInfoToEhFrame; } Reference::KindValue pointerKind() override { return pointer64; } uint32_t dwarfCompactUnwindType() override { return 0x04000000U; } const StubInfo &stubInfo() override { return _sStubInfo; } bool isNonCallBranch(const Reference &) override { return false; } bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; llvm::Error getReferenceInfo(const normalized::Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, 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 swap, 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 imageBase, 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 X86_64Kind: Reference::KindValue { invalid, /// for error condition // Kinds found in mach-o .o files: branch32, /// ex: call _foo ripRel32, /// ex: movq _foo(%rip), %rax ripRel32Minus1, /// ex: movb $0x12, _foo(%rip) ripRel32Minus2, /// ex: movw $0x1234, _foo(%rip) ripRel32Minus4, /// ex: movl $0x12345678, _foo(%rip) ripRel32Anon, /// ex: movq L1(%rip), %rax ripRel32Minus1Anon, /// ex: movb $0x12, L1(%rip) ripRel32Minus2Anon, /// ex: movw $0x1234, L1(%rip) ripRel32Minus4Anon, /// ex: movw $0x12345678, L1(%rip) ripRel32GotLoad, /// ex: movq _foo@GOTPCREL(%rip), %rax ripRel32Got, /// ex: pushq _foo@GOTPCREL(%rip) ripRel32Tlv, /// ex: movq _foo@TLVP(%rip), %rdi pointer64, /// ex: .quad _foo pointer64Anon, /// ex: .quad L1 delta64, /// ex: .quad _foo - . delta32, /// ex: .long _foo - . delta64Anon, /// ex: .quad L1 - . delta32Anon, /// ex: .long L1 - . negDelta64, /// ex: .quad . - _foo negDelta32, /// ex: .long . - _foo // Kinds introduced by Passes: ripRel32GotLoadNowLea, /// Target of GOT load is in linkage unit so /// "movq _foo@GOTPCREL(%rip), %rax" can be changed /// to "leaq _foo(%rip), %rax 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). 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. tlvInitSectionOffset /// Location contains offset tlv init-value atom /// within the __thread_data section. }; Reference::KindValue kindFromReloc(const normalized::Relocation &reloc); 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); }; const Registry::KindStrings ArchHandler_x86_64::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(branch32), LLD_KIND_STRING_ENTRY(ripRel32), LLD_KIND_STRING_ENTRY(ripRel32Minus1), LLD_KIND_STRING_ENTRY(ripRel32Minus2), LLD_KIND_STRING_ENTRY(ripRel32Minus4), LLD_KIND_STRING_ENTRY(ripRel32Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus1Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus2Anon), LLD_KIND_STRING_ENTRY(ripRel32Minus4Anon), LLD_KIND_STRING_ENTRY(ripRel32GotLoad), LLD_KIND_STRING_ENTRY(ripRel32GotLoadNowLea), LLD_KIND_STRING_ENTRY(ripRel32Got), LLD_KIND_STRING_ENTRY(ripRel32Tlv), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_ENTRY(pointer64), LLD_KIND_STRING_ENTRY(pointer64Anon), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(delta64), LLD_KIND_STRING_ENTRY(delta32Anon), LLD_KIND_STRING_ENTRY(delta64Anon), LLD_KIND_STRING_ENTRY(negDelta64), LLD_KIND_STRING_ENTRY(negDelta32), LLD_KIND_STRING_ENTRY(imageOffset), LLD_KIND_STRING_ENTRY(imageOffsetGot), LLD_KIND_STRING_ENTRY(unwindFDEToFunction), LLD_KIND_STRING_ENTRY(unwindInfoToEhFrame), LLD_KIND_STRING_ENTRY(tlvInitSectionOffset), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_x86_64::_sStubInfo = { "dyld_stub_binder", // Lazy pointer references { Reference::KindArch::x86_64, pointer64, 0, 0 }, { Reference::KindArch::x86_64, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::x86_64, pointer64, 0, 0 }, // x86_64 code alignment 2^1 1, // Stub size and code 6, { 0xff, 0x25, 0x00, 0x00, 0x00, 0x00 }, // jmp *lazyPointer { Reference::KindArch::x86_64, ripRel32, 2, 0 }, { false, 0, 0, 0 }, // Stub Helper size and code 10, { 0x68, 0x00, 0x00, 0x00, 0x00, // pushq $lazy-info-offset 0xE9, 0x00, 0x00, 0x00, 0x00 }, // jmp helperhelper { Reference::KindArch::x86_64, lazyImmediateLocation, 1, 0 }, { Reference::KindArch::x86_64, branch32, 6, 0 }, // Stub helper image cache content type DefinedAtom::typeNonLazyPointer, // Stub Helper-Common size and code 16, // Stub helper alignment 2, { 0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00, // leaq cache(%rip),%r11 0x41, 0x53, // push %r11 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *binder(%rip) 0x90 }, // nop { Reference::KindArch::x86_64, ripRel32, 3, 0 }, { false, 0, 0, 0 }, { Reference::KindArch::x86_64, ripRel32, 11, 0 }, { false, 0, 0, 0 } }; bool ArchHandler_x86_64::isCallSite(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::x86_64); return (ref.kindValue() == branch32); } bool ArchHandler_x86_64::isPointer(const Reference &ref) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return false; assert(ref.kindArch() == Reference::KindArch::x86_64); Reference::KindValue kind = ref.kindValue(); return (kind == pointer64 || kind == pointer64Anon); } bool ArchHandler_x86_64::isPairedReloc(const Relocation &reloc) { return (reloc.type == X86_64_RELOC_SUBTRACTOR); } Reference::KindValue ArchHandler_x86_64::kindFromReloc(const Relocation &reloc) { switch(relocPattern(reloc)) { case X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4: return branch32; case X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4: return ripRel32; case X86_64_RELOC_SIGNED | rPcRel | rLength4: return ripRel32Anon; case X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4: return ripRel32Minus1; case X86_64_RELOC_SIGNED_1 | rPcRel | rLength4: return ripRel32Minus1Anon; case X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4: return ripRel32Minus2; case X86_64_RELOC_SIGNED_2 | rPcRel | rLength4: return ripRel32Minus2Anon; case X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4: return ripRel32Minus4; case X86_64_RELOC_SIGNED_4 | rPcRel | rLength4: return ripRel32Minus4Anon; case X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4: return ripRel32GotLoad; case X86_64_RELOC_GOT | rPcRel | rExtern | rLength4: return ripRel32Got; case X86_64_RELOC_TLV | rPcRel | rExtern | rLength4: return ripRel32Tlv; case X86_64_RELOC_UNSIGNED | rExtern | rLength8: return pointer64; case X86_64_RELOC_UNSIGNED | rLength8: return pointer64Anon; default: return invalid; } } llvm::Error ArchHandler_x86_64::getReferenceInfo(const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool swap, FindAtomBySectionAndAddress atomFromAddress, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { *kind = kindFromReloc(reloc); if (*kind == invalid) return llvm::make_error("unknown type"); const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint64_t targetAddress; switch (*kind) { case branch32: case ripRel32: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return llvm::Error::success(); case ripRel32Minus1: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 1; return llvm::Error::success(); case ripRel32Minus2: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 2; return llvm::Error::success(); case ripRel32Minus4: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = (int32_t)*(const little32_t *)fixupContent + 4; return llvm::Error::success(); case ripRel32Anon: targetAddress = fixupAddress + 4 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus1Anon: targetAddress = fixupAddress + 5 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus2Anon: targetAddress = fixupAddress + 6 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32Minus4Anon: targetAddress = fixupAddress + 8 + *(const little32_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ripRel32GotLoad: case ripRel32Got: case ripRel32Tlv: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = *(const little32_t *)fixupContent; return llvm::Error::success(); case tlvInitSectionOffset: case pointer64: if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // If this is the 3rd pointer of a tlv-thunk (i.e. the pointer to the TLV's // initial value) we need to handle it specially. if (inAtom->contentType() == DefinedAtom::typeThunkTLV && offsetInAtom == 16) { *kind = tlvInitSectionOffset; assert(*addend == 0 && "TLV-init has non-zero addend?"); } else *addend = *(const little64_t *)fixupContent; return llvm::Error::success(); case pointer64Anon: targetAddress = *(const little64_t *)fixupContent; return atomFromAddress(reloc.symbol, targetAddress, target, addend); default: llvm_unreachable("bad reloc kind"); } } llvm::Error ArchHandler_x86_64::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]; uint64_t targetAddress; const lld::Atom *fromTarget; if (auto ec = atomFromSymbolIndex(reloc1.symbol, &fromTarget)) return ec; switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | X86_64_RELOC_UNSIGNED | rExtern | rLength8): { if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; uint64_t encodedAddend = (int64_t)*(const little64_t *)fixupContent; if (inAtom == fromTarget) { if (inAtom->contentType() == DefinedAtom::typeCFI) *kind = unwindFDEToFunction; else *kind = delta64; *addend = encodedAddend + offsetInAtom; } else if (inAtom == *target) { *kind = negDelta64; *addend = encodedAddend - offsetInAtom; *target = fromTarget; } else return llvm::make_error("Invalid pointer diff"); return llvm::Error::success(); } case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | X86_64_RELOC_UNSIGNED | rExtern | rLength4): { if (auto ec = atomFromSymbolIndex(reloc2.symbol, target)) return ec; uint32_t encodedAddend = (int32_t)*(const little32_t *)fixupContent; if (inAtom == fromTarget) { *kind = delta32; *addend = encodedAddend + offsetInAtom; } else if (inAtom == *target) { *kind = negDelta32; *addend = encodedAddend - offsetInAtom; *target = fromTarget; } else return llvm::make_error("Invalid pointer diff"); return llvm::Error::success(); } case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength8) << 16 | X86_64_RELOC_UNSIGNED | rLength8): if (fromTarget != inAtom) return llvm::make_error("pointer diff not in base atom"); *kind = delta64Anon; targetAddress = offsetInAtom + (int64_t)*(const little64_t *)fixupContent; return atomFromAddress(reloc2.symbol, targetAddress, target, addend); case ((X86_64_RELOC_SUBTRACTOR | rExtern | rLength4) << 16 | X86_64_RELOC_UNSIGNED | rLength4): if (fromTarget != inAtom) return llvm::make_error("pointer diff not in base atom"); *kind = delta32Anon; targetAddress = offsetInAtom + (int32_t)*(const little32_t *)fixupContent; return atomFromAddress(reloc2.symbol, targetAddress, target, addend); default: return llvm::make_error("unknown pair"); } } void ArchHandler_x86_64::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. for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); 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); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, imageBaseAddress, findSectionAddress); } } } void ArchHandler_x86_64::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::x86_64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: case ripRel32: case ripRel32Anon: case ripRel32Got: case ripRel32GotLoad: case ripRel32Tlv: *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case pointer64: case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; case tlvInitSectionOffset: *loc64 = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); return; case ripRel32Minus1: case ripRel32Minus1Anon: *loc32 = targetAddress - (fixupAddress + 5) + ref.addend(); return; case ripRel32Minus2: case ripRel32Minus2Anon: *loc32 = targetAddress - (fixupAddress + 6) + ref.addend(); return; case ripRel32Minus4: case ripRel32Minus4Anon: *loc32 = targetAddress - (fixupAddress + 8) + ref.addend(); return; case delta32: case delta32Anon: *loc32 = targetAddress - fixupAddress + ref.addend(); return; case delta64: case delta64Anon: case unwindFDEToFunction: *loc64 = targetAddress - fixupAddress + ref.addend(); return; case ripRel32GotLoadNowLea: // Change MOVQ to LEA assert(loc[-2] == 0x8B); loc[-2] = 0x8D; *loc32 = targetAddress - (fixupAddress + 4) + ref.addend(); return; case negDelta64: *loc64 = fixupAddress - targetAddress + ref.addend(); return; case negDelta32: *loc32 = fixupAddress - targetAddress + ref.addend(); return; case lazyPointer: // Do nothing return; case lazyImmediateLocation: *loc32 = ref.addend(); return; case imageOffset: case imageOffsetGot: *loc32 = (targetAddress - imageBaseAddress) + ref.addend(); return; case unwindInfoToEhFrame: { uint64_t val = targetAddress - findSectionAddress(*ref.target()) + ref.addend(); assert(val < 0xffffffU && "offset in __eh_frame too large"); *loc32 = (*loc32 & 0xff000000U) | val; return; } case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("invalid x86_64 Reference Kind"); } void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::x86_64); ulittle32_t *loc32 = reinterpret_cast(loc); ulittle64_t *loc64 = reinterpret_cast(loc); switch (static_cast(ref.kindValue())) { case branch32: case ripRel32: case ripRel32Got: case ripRel32GotLoad: case ripRel32Tlv: *loc32 = ref.addend(); return; case ripRel32Anon: *loc32 = (targetAddress - (fixupAddress + 4)) + ref.addend(); return; case tlvInitSectionOffset: case pointer64: *loc64 = ref.addend(); return; case pointer64Anon: *loc64 = targetAddress + ref.addend(); return; case ripRel32Minus1: *loc32 = ref.addend() - 1; return; case ripRel32Minus1Anon: *loc32 = (targetAddress - (fixupAddress + 5)) + ref.addend(); return; case ripRel32Minus2: *loc32 = ref.addend() - 2; return; case ripRel32Minus2Anon: *loc32 = (targetAddress - (fixupAddress + 6)) + ref.addend(); return; case ripRel32Minus4: *loc32 = ref.addend() - 4; return; case ripRel32Minus4Anon: *loc32 = (targetAddress - (fixupAddress + 8)) + ref.addend(); return; case delta32: *loc32 = ref.addend() + inAtomAddress - fixupAddress; return; case delta32Anon: // The value we write here should be the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: // _base: ... // Lfixup: .quad Ltarget - . // ... // Ltarget: // // Then we want to encode the value (Ltarget + addend) - (LFixup - _base) *loc32 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress); return; case delta64: *loc64 = ref.addend() + inAtomAddress - fixupAddress; return; case delta64Anon: // The value we write here should be the delta to the target // after taking in to account the difference from the fixup back to the // last defined label // ie, if we have: // _base: ... // Lfixup: .quad Ltarget - . // ... // Ltarget: // // Then we want to encode the value (Ltarget + addend) - (LFixup - _base) *loc64 = (targetAddress + ref.addend()) - (fixupAddress - inAtomAddress); return; case negDelta64: *loc64 = ref.addend() + fixupAddress - inAtomAddress; return; case negDelta32: *loc32 = ref.addend() + fixupAddress - inAtomAddress; return; case ripRel32GotLoadNowLea: llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); return; case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; case imageOffset: case imageOffsetGot: case unwindInfoToEhFrame: llvm_unreachable("fixup implies __unwind_info"); return; case unwindFDEToFunction: // Do nothing for now return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown x86_64 Reference Kind"); } void ArchHandler_x86_64::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::x86_64); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); switch (static_cast(ref.kindValue())) { case branch32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_BRANCH | rPcRel | rExtern | rLength4); return; case ripRel32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED | rPcRel | rExtern | rLength4 ); return; case ripRel32Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED | rPcRel | rLength4 ); return; case ripRel32Got: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT | rPcRel | rExtern | rLength4 ); return; case ripRel32GotLoad: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_GOT_LOAD | rPcRel | rExtern | rLength4 ); return; case ripRel32Tlv: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_TLV | rPcRel | rExtern | rLength4 ); return; case tlvInitSectionOffset: case pointer64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8); return; case pointer64Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength8); return; case ripRel32Minus1: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_1 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus1Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_1 | rPcRel | rLength4 ); return; case ripRel32Minus2: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_2 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus2Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_2 | rPcRel | rLength4 ); return; case ripRel32Minus4: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_4 | rPcRel | rExtern | rLength4 ); return; case ripRel32Minus4Anon: appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_SIGNED_4 | rPcRel | rLength4 ); return; case delta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case delta32Anon: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength4 ); return; case delta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8 ); return; case delta64Anon: appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()), 0, X86_64_RELOC_UNSIGNED | rLength8 ); return; case unwindFDEToFunction: case unwindInfoToEhFrame: return; case negDelta32: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength4 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength4 ); return; case negDelta64: appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, X86_64_RELOC_SUBTRACTOR | rExtern | rLength8 ); appendReloc(relocs, sectionOffset, symbolIndexForAtom(atom), 0, X86_64_RELOC_UNSIGNED | rExtern | rLength8 ); return; case ripRel32GotLoadNowLea: llvm_unreachable("ripRel32GotLoadNowLea implies GOT pass was run"); return; case lazyPointer: case lazyImmediateLocation: llvm_unreachable("lazy reference kind implies Stubs pass was run"); return; case imageOffset: case imageOffsetGot: llvm_unreachable("__unwind_info references should have been resolved"); return; case invalid: // Fall into llvm_unreachable(). break; } llvm_unreachable("unknown x86_64 Reference Kind"); } std::unique_ptr ArchHandler::create_x86_64() { return std::unique_ptr(new ArchHandler_x86_64()); } } // namespace mach_o } // namespace lld