//===- lib/FileFormat/MachO/ArchHandler_arm.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::little32_t; class ArchHandler_arm : public ArchHandler { public: ArchHandler_arm() = default; ~ArchHandler_arm() override = default; const Registry::KindStrings *kindStrings() override { return _sKindStrings; } Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } const ArchHandler::StubInfo &stubInfo() override; bool isCallSite(const Reference &) override; bool isPointer(const Reference &) override; bool isPairedReloc(const normalized::Relocation &) override; bool isNonCallBranch(const Reference &) override; bool needsCompactUnwind() override { return false; } Reference::KindValue imageOffsetKind() override { return invalid; } Reference::KindValue imageOffsetKindIndirect() override { return invalid; } Reference::KindValue unwindRefToPersonalityFunctionKind() override { return invalid; } Reference::KindValue unwindRefToCIEKind() override { return invalid; } Reference::KindValue unwindRefToFunctionKind() override { return invalid; } Reference::KindValue unwindRefToEhFrameKind() override { return invalid; } Reference::KindValue pointerKind() override { return invalid; } uint32_t dwarfCompactUnwindType() override { // FIXME return -1; } 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; 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, FindSectionIndexForAtom, FindAddressForAtom, normalized::Relocations &) override; void addAdditionalReferences(MachODefinedAtom &atom) override; bool isDataInCodeTransition(Reference::KindValue refKind) override { switch (refKind) { case modeThumbCode: case modeArmCode: case modeData: return true; default: return false; break; } } Reference::KindValue dataInCodeTransitionStart( const MachODefinedAtom &atom) override { return modeData; } Reference::KindValue dataInCodeTransitionEnd( const MachODefinedAtom &atom) override { return atom.isThumb() ? modeThumbCode : modeArmCode; } bool isThumbFunction(const DefinedAtom &atom) override; const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, const DefinedAtom &) override; private: friend class Thumb2ToArmShimAtom; friend class ArmToThumbShimAtom; static const Registry::KindStrings _sKindStrings[]; static const StubInfo _sStubInfoArmPIC; enum ArmKind : Reference::KindValue { invalid, /// for error condition modeThumbCode, /// Content starting at this offset is thumb. modeArmCode, /// Content starting at this offset is arm. modeData, /// Content starting at this offset is data. // Kinds found in mach-o .o files: thumb_bl22, /// ex: bl _foo thumb_b22, /// ex: b _foo thumb_movw, /// ex: movw r1, :lower16:_foo thumb_movt, /// ex: movt r1, :lower16:_foo thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) arm_bl24, /// ex: bl _foo arm_b24, /// ex: b _foo arm_movw, /// ex: movw r1, :lower16:_foo arm_movt, /// ex: movt r1, :lower16:_foo arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) pointer32, /// ex: .long _foo delta32, /// ex: .long _foo - . // Kinds introduced by Passes: lazyPointer, /// Location contains a lazy pointer. lazyImmediateLocation, /// Location contains immediate value used in stub. }; // Utility functions for inspecting/updating instructions. static bool isThumbMovw(uint32_t instruction); static bool isThumbMovt(uint32_t instruction); static bool isArmMovw(uint32_t instruction); static bool isArmMovt(uint32_t instruction); static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); static int32_t getDisplacementFromArmBranch(uint32_t instruction); static uint16_t getWordFromThumbMov(uint32_t instruction); static uint16_t getWordFromArmMov(uint32_t instruction); static uint32_t clearThumbBit(uint32_t value, const Atom *target); static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, bool targetIsThumb); static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, int32_t disp, bool targetThumb); static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); StringRef stubName(const DefinedAtom &); bool useExternalRelocationTo(const Atom &target); void applyFixupFinal(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb); void applyFixupRelocatable(const Reference &ref, uint8_t *location, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb); }; //===----------------------------------------------------------------------===// // ArchHandler_arm //===----------------------------------------------------------------------===// const Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { LLD_KIND_STRING_ENTRY(invalid), LLD_KIND_STRING_ENTRY(modeThumbCode), LLD_KIND_STRING_ENTRY(modeArmCode), LLD_KIND_STRING_ENTRY(modeData), LLD_KIND_STRING_ENTRY(thumb_bl22), LLD_KIND_STRING_ENTRY(thumb_b22), LLD_KIND_STRING_ENTRY(thumb_movw), LLD_KIND_STRING_ENTRY(thumb_movt), LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), LLD_KIND_STRING_ENTRY(arm_bl24), LLD_KIND_STRING_ENTRY(arm_b24), LLD_KIND_STRING_ENTRY(arm_movw), LLD_KIND_STRING_ENTRY(arm_movt), LLD_KIND_STRING_ENTRY(arm_movw_funcRel), LLD_KIND_STRING_ENTRY(arm_movt_funcRel), LLD_KIND_STRING_ENTRY(pointer32), LLD_KIND_STRING_ENTRY(delta32), LLD_KIND_STRING_ENTRY(lazyPointer), LLD_KIND_STRING_ENTRY(lazyImmediateLocation), LLD_KIND_STRING_END }; const ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { "dyld_stub_binder", // References in lazy pointer { Reference::KindArch::ARM, pointer32, 0, 0 }, { Reference::KindArch::ARM, lazyPointer, 0, 0 }, // GOT pointer to dyld_stub_binder { Reference::KindArch::ARM, pointer32, 0, 0 }, // arm code alignment 2^2 2, // Stub size and code 16, { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) { Reference::KindArch::ARM, delta32, 12, 0 }, { false, 0, 0, 0 }, // Stub Helper size and code 12, { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] 0x00, 0x00, 0x00, 0xEA, // b _helperhelper 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, { Reference::KindArch::ARM, arm_b24, 4, 0 }, // Stub helper image cache content type DefinedAtom::typeGOT, // Stub Helper-Common size and code 36, // Stub helper alignment 2, { // push lazy-info-offset 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! // push address of dyld_mageLoaderCache 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! // jump through dyld_stub_binder 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) { Reference::KindArch::ARM, delta32, 28, 0xC }, { false, 0, 0, 0 }, { Reference::KindArch::ARM, delta32, 32, 0x04 }, { false, 0, 0, 0 } }; const ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { // If multiple kinds of stubs are supported, select which StubInfo here. return _sStubInfoArmPIC; } bool ArchHandler_arm::isCallSite(const Reference &ref) { switch (ref.kindValue()) { case thumb_b22: case thumb_bl22: case arm_b24: case arm_bl24: return true; default: return false; } } bool ArchHandler_arm::isPointer(const Reference &ref) { return (ref.kindValue() == pointer32); } bool ArchHandler_arm::isNonCallBranch(const Reference &ref) { switch (ref.kindValue()) { case thumb_b22: case arm_b24: return true; default: return false; } } bool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { switch (reloc.type) { case ARM_RELOC_SECTDIFF: case ARM_RELOC_LOCAL_SECTDIFF: case ARM_RELOC_HALF_SECTDIFF: case ARM_RELOC_HALF: return true; default: return false; } } /// Trace references from stub atom to lazy pointer to target and get its name. StringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { assert(stubAtom.contentType() == DefinedAtom::typeStub); for (const Reference *ref : stubAtom) { if (const DefinedAtom* lp = dyn_cast(ref->target())) { if (lp->contentType() != DefinedAtom::typeLazyPointer) continue; for (const Reference *ref2 : *lp) { if (ref2->kindValue() != lazyPointer) continue; return ref2->target()->name(); } } } return "stub"; } /// Extract displacement from an ARM b/bl/blx instruction. int32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { // Sign-extend imm24 int32_t displacement = (instruction & 0x00FFFFFF) << 2; if ((displacement & 0x02000000) != 0) displacement |= 0xFC000000; // If this is BLX and H bit set, add 2. if ((instruction & 0xFF000000) == 0xFB000000) displacement += 2; return displacement; } /// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. uint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, int32_t displacement, bool targetIsThumb) { assert((displacement <= 33554428) && (displacement > (-33554432)) && "arm branch out of range"); bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); uint32_t newInstruction = (instruction & 0xFF000000); uint32_t h = 0; if (targetIsThumb) { // Force use of BLX. newInstruction = 0xFA000000; if (!is_blx) { assert(((instruction & 0xF0000000) == 0xE0000000) && "no conditional arm blx"); assert(((instruction & 0xFF000000) == 0xEB000000) && "no arm pc-rel BX instruction"); } if (displacement & 2) h = 1; } else { // Force use of B/BL. if (is_blx) newInstruction = 0xEB000000; } newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); return newInstruction; } /// Extract displacement from a thumb b/bl/blx instruction. int32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr) { bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); uint32_t s = (instruction >> 10) & 0x1; uint32_t j1 = (instruction >> 29) & 0x1; uint32_t j2 = (instruction >> 27) & 0x1; uint32_t imm10 = instruction & 0x3FF; uint32_t imm11 = (instruction >> 16) & 0x7FF; uint32_t i1 = (j1 == s); uint32_t i2 = (j2 == s); uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); int32_t sdis = dis; int32_t result = s ? (sdis | 0xFE000000) : sdis; if (is_blx && (instrAddr & 0x2)) { // The thumb blx instruction always has low bit of imm11 as zero. The way // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that // the blx instruction always 4-byte aligns the pc before adding the // displacement from the blx. We must emulate that when decoding this. result -= 2; } return result; } /// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. uint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, int32_t displacement, bool targetIsThumb) { assert((displacement <= 16777214) && (displacement > (-16777216)) && "thumb branch out of range"); bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); bool is_b = ((instruction & 0xD000F800) == 0x9000F000); uint32_t newInstruction = (instruction & 0xD000F800); if (is_bl || is_blx) { if (targetIsThumb) { newInstruction = 0xD000F000; // Use bl } else { newInstruction = 0xC000F000; // Use blx // See note in getDisplacementFromThumbBranch() about blx. if (instrAddr & 0x2) displacement += 2; } } else if (is_b) { assert(targetIsThumb && "no pc-rel thumb branch instruction that " "switches to arm mode"); } else { llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); } uint32_t s = (uint32_t)(displacement >> 24) & 0x1; uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; uint32_t j1 = (i1 == s); uint32_t j2 = (i2 == s); uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; uint32_t firstDisp = (s << 10) | imm10; newInstruction |= (nextDisp << 16) | firstDisp; return newInstruction; } bool ArchHandler_arm::isThumbMovw(uint32_t instruction) { return (instruction & 0x8000FBF0) == 0x0000F240; } bool ArchHandler_arm::isThumbMovt(uint32_t instruction) { return (instruction & 0x8000FBF0) == 0x0000F2C0; } bool ArchHandler_arm::isArmMovw(uint32_t instruction) { return (instruction & 0x0FF00000) == 0x03000000; } bool ArchHandler_arm::isArmMovt(uint32_t instruction) { return (instruction & 0x0FF00000) == 0x03400000; } uint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { assert(isThumbMovw(instruction) || isThumbMovt(instruction)); uint32_t i = ((instruction & 0x00000400) >> 10); uint32_t imm4 = (instruction & 0x0000000F); uint32_t imm3 = ((instruction & 0x70000000) >> 28); uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; } uint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { assert(isArmMovw(instruction) || isArmMovt(instruction)); uint32_t imm4 = ((instruction & 0x000F0000) >> 16); uint32_t imm12 = (instruction & 0x00000FFF); return (imm4 << 12) | imm12; } uint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { assert(isThumbMovw(instr) || isThumbMovt(instr)); uint32_t imm4 = (word & 0xF000) >> 12; uint32_t i = (word & 0x0800) >> 11; uint32_t imm3 = (word & 0x0700) >> 8; uint32_t imm8 = word & 0x00FF; return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); } uint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { assert(isArmMovw(instr) || isArmMovt(instr)); uint32_t imm4 = (word & 0xF000) >> 12; uint32_t imm12 = word & 0x0FFF; return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; } uint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { // The assembler often adds one to the address of a thumb function. // We need to undo that so it does not look like an addend. if (value & 1) { if (isa(target)) { const MachODefinedAtom *machoTarget = reinterpret_cast(target); if (machoTarget->isThumb()) value &= -2; // mask off thumb-bit } } return value; } llvm::Error ArchHandler_arm::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]; uint64_t targetAddress; uint32_t instruction = *(const ulittle32_t *)fixupContent; int32_t displacement; switch (relocPattern(reloc)) { case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: // ex: bl _foo (and _foo is undefined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); *addend = fixupAddress + 4 + displacement; return llvm::Error(); case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); targetAddress = fixupAddress + 4 + displacement; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: // ex: bl _foo+4 (and _foo is defined) if ((instruction & 0xD000F800) == 0x9000F000) *kind = thumb_b22; else *kind = thumb_bl22; displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); targetAddress = fixupAddress + 4 + displacement; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; // reloc.value is target atom's address. Instruction contains branch // to atom+addend. *addend += (targetAddress - reloc.value); return llvm::Error(); case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: // ex: bl _foo (and _foo is undefined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; // Instruction contains branch to addend. displacement = getDisplacementFromArmBranch(instruction); *addend = fixupAddress + 8 + displacement; return llvm::Error(); case ARM_RELOC_BR24 | rPcRel | rLength4: // ex: bl _foo (and _foo is defined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; displacement = getDisplacementFromArmBranch(instruction); targetAddress = fixupAddress + 8 + displacement; return atomFromAddress(reloc.symbol, targetAddress, target, addend); case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: // ex: bl _foo+4 (and _foo is defined) if (((instruction & 0x0F000000) == 0x0A000000) && ((instruction & 0xF0000000) != 0xF0000000)) *kind = arm_b24; else *kind = arm_bl24; displacement = getDisplacementFromArmBranch(instruction); targetAddress = fixupAddress + 8 + displacement; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; // reloc.value is target atom's address. Instruction contains branch // to atom+addend. *addend += (targetAddress - reloc.value); return llvm::Error(); case ARM_RELOC_VANILLA | rExtern | rLength4: // ex: .long _foo (and _foo is undefined) *kind = pointer32; if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) return ec; *addend = instruction; return llvm::Error(); case ARM_RELOC_VANILLA | rLength4: // ex: .long _foo (and _foo is defined) *kind = pointer32; if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend)) return ec; *addend = clearThumbBit((uint32_t) * addend, *target); return llvm::Error(); case ARM_RELOC_VANILLA | rScattered | rLength4: // ex: .long _foo+a (and _foo is defined) *kind = pointer32; if (auto ec = atomFromAddress(0, reloc.value, target, addend)) return ec; *addend += (clearThumbBit(instruction, *target) - reloc.value); return llvm::Error(); default: return llvm::make_error("unsupported arm relocation type"); } return llvm::Error(); } llvm::Error ArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, const normalized::Relocation &reloc2, const DefinedAtom *inAtom, uint32_t offsetInAtom, uint64_t fixupAddress, bool isBig, bool scatterable, FindAtomBySectionAndAddress atomFromAddr, FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, const lld::Atom **target, Reference::Addend *addend) { bool pointerDiff = false; bool funcRel; bool top; bool thumbReloc; switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rScattered | rLenThmbLo): // ex: movw r1, :lower16:(_x-L1) [thumb mode] *kind = thumb_movw_funcRel; funcRel = true; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rScattered | rLenThmbHi): // ex: movt r1, :upper16:(_x-L1) [thumb mode] *kind = thumb_movt_funcRel; funcRel = true; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | ARM_RELOC_PAIR | rScattered | rLenArmLo): // ex: movw r1, :lower16:(_x-L1) [arm mode] *kind = arm_movw_funcRel; funcRel = true; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | ARM_RELOC_PAIR | rScattered | rLenArmHi): // ex: movt r1, :upper16:(_x-L1) [arm mode] *kind = arm_movt_funcRel; funcRel = true; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_x [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_x [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_x [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_x [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_x+a [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_x+a [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_x+a [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_x+a [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | ARM_RELOC_PAIR | rLenThmbLo): // ex: movw r1, :lower16:_undef [thumb mode] *kind = thumb_movw; funcRel = false; top = false; thumbReloc = true; break; case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | ARM_RELOC_PAIR | rLenThmbHi): // ex: movt r1, :upper16:_undef [thumb mode] *kind = thumb_movt; funcRel = false; top = true; thumbReloc = true; break; case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | ARM_RELOC_PAIR | rLenArmLo): // ex: movw r1, :lower16:_undef [arm mode] *kind = arm_movw; funcRel = false; top = false; thumbReloc = false; break; case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | ARM_RELOC_PAIR | rLenArmHi): // ex: movt r1, :upper16:_undef [arm mode] *kind = arm_movt; funcRel = false; top = true; thumbReloc = false; break; case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | ARM_RELOC_PAIR | rScattered | rLength4): case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | ARM_RELOC_PAIR | rScattered | rLength4): // ex: .long _foo - . pointerDiff = true; break; default: return llvm::make_error("unsupported arm relocation pair"); } const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; uint32_t instruction = *(const ulittle32_t *)fixupContent; uint32_t value; uint32_t fromAddress; uint32_t toAddress; uint16_t instruction16; uint16_t other16; const lld::Atom *fromTarget; Reference::Addend offsetInTo; Reference::Addend offsetInFrom; if (pointerDiff) { toAddress = reloc1.value; fromAddress = reloc2.value; if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (scatterable && (fromTarget != inAtom)) return llvm::make_error( "SECTDIFF relocation where subtrahend label is not in atom"); *kind = delta32; value = clearThumbBit(instruction, *target); *addend = (int32_t)(value - (toAddress - fixupAddress)); } else if (funcRel) { toAddress = reloc1.value; fromAddress = reloc2.value; if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) return ec; if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) return ec; if (fromTarget != inAtom) return llvm::make_error("ARM_RELOC_HALF_SECTDIFF relocation" " where subtrahend label is not in atom"); other16 = (reloc2.offset & 0xFFFF); if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isThumbMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isArmMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } if (top) value = (instruction16 << 16) | other16; else value = (other16 << 16) | instruction16; value = clearThumbBit(value, *target); int64_t ta = (int64_t) value - (toAddress - fromAddress); *addend = ta - offsetInFrom; return llvm::Error(); } else { uint32_t sectIndex; if (thumbReloc) { if (top) { if (!isThumbMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isThumbMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromThumbMov(instruction); } else { if (top) { if (!isArmMovt(instruction)) return llvm::make_error("expected movt instruction"); } else { if (!isArmMovw(instruction)) return llvm::make_error("expected movw instruction"); } instruction16 = getWordFromArmMov(instruction); } other16 = (reloc2.offset & 0xFFFF); if (top) value = (instruction16 << 16) | other16; else value = (other16 << 16) | instruction16; if (reloc1.isExtern) { if (auto ec = atomFromSymbolIndex(reloc1.symbol, target)) return ec; *addend = value; } else { if (reloc1.scattered) { toAddress = reloc1.value; sectIndex = 0; } else { toAddress = value; sectIndex = reloc1.symbol; } if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo)) return ec; *addend = value - toAddress; } } return llvm::Error(); } void ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::ARM); ulittle32_t *loc32 = reinterpret_cast(loc); int32_t displacement; uint16_t value16; uint32_t value32; switch (static_cast(ref.kindValue())) { case modeThumbCode: thumbMode = true; break; case modeArmCode: thumbMode = false; break; case modeData: break; case thumb_b22: case thumb_bl22: assert(thumbMode); displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement, targetIsThumb); *loc32 = value32; break; case thumb_movw: assert(thumbMode); value16 = (targetAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt: assert(thumbMode); value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movw_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case arm_b24: case arm_bl24: assert(!thumbMode); displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); *loc32 = value32; break; case arm_movw: assert(!thumbMode); value16 = (targetAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt: assert(!thumbMode); value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movw_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; if (targetIsThumb) value16 |= 1; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case pointer32: if (targetIsThumb) *loc32 = targetAddress + ref.addend() + 1; else *loc32 = targetAddress + ref.addend(); break; case delta32: if (targetIsThumb) *loc32 = targetAddress - fixupAddress + ref.addend() + 1; else *loc32 = targetAddress - fixupAddress + ref.addend(); break; case lazyPointer: // do nothing break; case lazyImmediateLocation: *loc32 = ref.addend(); break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::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. bool thumbMode = false; for (const Reference *ref : atom) { uint32_t offset = ref->offsetInAtom(); const Atom *target = ref->target(); uint64_t targetAddress = 0; bool targetIsThumb = false; if (const DefinedAtom *defTarg = dyn_cast(target)) { targetAddress = findAddress(*target); targetIsThumb = isThumbFunction(*defTarg); } uint64_t atomAddress = findAddress(atom); uint64_t fixupAddress = atomAddress + offset; if (relocatable) { applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, thumbMode, targetIsThumb); } else { applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, targetAddress, atomAddress, thumbMode, targetIsThumb); } } } bool ArchHandler_arm::useExternalRelocationTo(const Atom &target) { // Undefined symbols are referenced via external relocations. if (isa(&target)) return true; if (const DefinedAtom *defAtom = dyn_cast(&target)) { switch (defAtom->merge()) { case DefinedAtom::mergeAsTentative: // Tentative definitions are referenced via external relocations. return true; case DefinedAtom::mergeAsWeak: case DefinedAtom::mergeAsWeakAndAddressUsed: // Global weak-defs are referenced via external relocations. return (defAtom->scope() == DefinedAtom::scopeGlobal); default: break; } } // Everything else is reference via an internal relocation. return false; } void ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc, uint64_t fixupAddress, uint64_t targetAddress, uint64_t inAtomAddress, bool &thumbMode, bool targetIsThumb) { if (ref.kindNamespace() != Reference::KindNamespace::mach_o) return; assert(ref.kindArch() == Reference::KindArch::ARM); bool useExternalReloc = useExternalRelocationTo(*ref.target()); ulittle32_t *loc32 = reinterpret_cast(loc); int32_t displacement; uint16_t value16; uint32_t value32; bool targetIsUndef = isa(ref.target()); switch (static_cast(ref.kindValue())) { case modeThumbCode: thumbMode = true; break; case modeArmCode: thumbMode = false; break; case modeData: break; case thumb_b22: case thumb_bl22: assert(thumbMode); if (useExternalReloc) displacement = (ref.addend() - (fixupAddress + 4)); else displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, displacement, targetIsUndef || targetIsThumb); *loc32 = value32; break; case thumb_movw: assert(thumbMode); if (useExternalReloc) value16 = ref.addend() & 0xFFFF; else value16 = (targetAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt: assert(thumbMode); if (useExternalReloc) value16 = ref.addend() >> 16; else value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movw_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromThumbMov(*loc32, value16); break; case thumb_movt_funcRel: assert(thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromThumbMov(*loc32, value16); break; case arm_b24: case arm_bl24: assert(!thumbMode); if (useExternalReloc) displacement = (ref.addend() - (fixupAddress + 8)); else displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); *loc32 = value32; break; case arm_movw: assert(!thumbMode); if (useExternalReloc) value16 = ref.addend() & 0xFFFF; else value16 = (targetAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt: assert(!thumbMode); if (useExternalReloc) value16 = ref.addend() >> 16; else value16 = (targetAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movw_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; *loc32 = setWordFromArmMov(*loc32, value16); break; case arm_movt_funcRel: assert(!thumbMode); value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; *loc32 = setWordFromArmMov(*loc32, value16); break; case pointer32: *loc32 = targetAddress + ref.addend(); break; case delta32: *loc32 = targetAddress - fixupAddress + ref.addend(); break; case lazyPointer: case lazyImmediateLocation: // do nothing break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::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::ARM); uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); bool useExternalReloc = useExternalRelocationTo(*ref.target()); uint32_t targetAtomAddress; uint32_t fromAtomAddress; uint16_t other16; switch (static_cast(ref.kindValue())) { case modeThumbCode: case modeArmCode: case modeData: // Do nothing. break; case thumb_b22: case thumb_bl22: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_THUMB_RELOC_BR22 | rPcRel | rLength4); } break; case thumb_movw: if (useExternalReloc) { other16 = ref.addend() >> 16; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } else { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenThmbLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbLo); } } break; case thumb_movt: if (useExternalReloc) { other16 = ref.addend() & 0xFFFF; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } else { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenThmbHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenThmbHi); } } break; case thumb_movw_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenThmbLo); break; case thumb_movt_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenThmbHi); break; case arm_b24: case arm_bl24: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_BR24 | rExtern | rPcRel | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_BR24 | rScattered | rPcRel | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_BR24 | rPcRel | rLength4); } break; case arm_movw: if (useExternalReloc) { other16 = ref.addend() >> 16; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } else { other16 = (targetAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenArmLo); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmLo); } } break; case arm_movt: if (useExternalReloc) { other16 = ref.addend() & 0xFFFF; appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_HALF | rExtern | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } else { targetAtomAddress = addressForAtom(*ref.target()); if (ref.addend() != 0) { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF | rScattered | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } else { other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_HALF | rLenArmHi); appendReloc(relocs, other16, 0, 0, ARM_RELOC_PAIR | rLenArmHi); } } break; case arm_movw_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenArmLo); break; case arm_movt_funcRel: fromAtomAddress = addressForAtom(atom); targetAtomAddress = addressForAtom(*ref.target()); other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; appendReloc(relocs, sectionOffset, 0, targetAtomAddress, ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi); appendReloc(relocs, other16, 0, fromAtomAddress, ARM_RELOC_PAIR | rScattered | rLenArmHi); break; case pointer32: if (useExternalReloc) { appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, ARM_RELOC_VANILLA | rExtern | rLength4); } else { if (ref.addend() != 0) appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_VANILLA | rScattered | rLength4); else appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, ARM_RELOC_VANILLA | rLength4); } break; case delta32: appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), ARM_RELOC_SECTDIFF | rScattered | rLength4); appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + ref.offsetInAtom(), ARM_RELOC_PAIR | rScattered | rLength4); break; case lazyPointer: case lazyImmediateLocation: // do nothing break; case invalid: llvm_unreachable("invalid ARM Reference Kind"); break; } } void ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { if (atom.isThumb()) { atom.addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0); } } bool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { for (const Reference *ref : atom) { if (ref->offsetInAtom() != 0) return false; if (ref->kindNamespace() != Reference::KindNamespace::mach_o) continue; assert(ref->kindArch() == Reference::KindArch::ARM); if (ref->kindValue() == modeThumbCode) return true; } return false; } class Thumb2ToArmShimAtom : public SimpleDefinedAtom { public: Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, const DefinedAtom &target) : SimpleDefinedAtom(file) { addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::modeThumbCode, 0, this, 0); addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::delta32, 8, &target, 0); std::string name = std::string(targetName) + "$shim"; StringRef tmp(name); _name = tmp.copy(file.allocator()); } ~Thumb2ToArmShimAtom() override = default; StringRef name() const override { return _name; } ContentType contentType() const override { return DefinedAtom::typeCode; } Alignment alignment() const override { return 4; } uint64_t size() const override { return 12; } ContentPermissions permissions() const override { return DefinedAtom::permR_X; } ArrayRef rawContent() const override { static const uint8_t bytes[] = { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4 0xFF, 0x44, // add ip, pc, ip 0x60, 0x47, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }; // .long target - this assert(sizeof(bytes) == size()); return llvm::makeArrayRef(bytes, sizeof(bytes)); } private: StringRef _name; }; class ArmToThumbShimAtom : public SimpleDefinedAtom { public: ArmToThumbShimAtom(MachOFile &file, StringRef targetName, const DefinedAtom &target) : SimpleDefinedAtom(file) { addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, ArchHandler_arm::delta32, 12, &target, 0); std::string name = std::string(targetName) + "$shim"; StringRef tmp(name); _name = tmp.copy(file.allocator()); } ~ArmToThumbShimAtom() override = default; StringRef name() const override { return _name; } ContentType contentType() const override { return DefinedAtom::typeCode; } Alignment alignment() const override { return 4; } uint64_t size() const override { return 16; } ContentPermissions permissions() const override { return DefinedAtom::permR_X; } ArrayRef rawContent() const override { static const uint8_t bytes[] = { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] 0x00, 0x00, 0x00, 0x00 }; // .long target - this assert(sizeof(bytes) == size()); return llvm::makeArrayRef(bytes, sizeof(bytes)); } private: StringRef _name; }; const DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, bool thumbToArm, const DefinedAtom &target) { bool isStub = (target.contentType() == DefinedAtom::typeStub); StringRef targetName = isStub ? stubName(target) : target.name(); if (thumbToArm) return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); else return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); } std::unique_ptr ArchHandler::create_arm() { return std::unique_ptr(new ArchHandler_arm()); } } // namespace mach_o } // namespace lld