ArchHandler_arm.cpp revision 280461
1280461Sdim//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===// 2280461Sdim// 3280461Sdim// The LLVM Linker 4280461Sdim// 5280461Sdim// This file is distributed under the University of Illinois Open Source 6280461Sdim// License. See LICENSE.TXT for details. 7280461Sdim// 8280461Sdim//===----------------------------------------------------------------------===// 9280461Sdim 10280461Sdim#include "ArchHandler.h" 11280461Sdim#include "Atoms.h" 12280461Sdim#include "MachONormalizedFileBinaryUtils.h" 13280461Sdim#include "llvm/ADT/StringRef.h" 14280461Sdim#include "llvm/ADT/StringSwitch.h" 15280461Sdim#include "llvm/ADT/Triple.h" 16280461Sdim#include "llvm/Support/Endian.h" 17280461Sdim#include "llvm/Support/ErrorHandling.h" 18280461Sdim 19280461Sdimusing namespace llvm::MachO; 20280461Sdimusing namespace lld::mach_o::normalized; 21280461Sdim 22280461Sdimnamespace lld { 23280461Sdimnamespace mach_o { 24280461Sdim 25280461Sdimusing llvm::support::ulittle32_t; 26280461Sdimusing llvm::support::little32_t; 27280461Sdim 28280461Sdim 29280461Sdimclass ArchHandler_arm : public ArchHandler { 30280461Sdimpublic: 31280461Sdim ArchHandler_arm(); 32280461Sdim virtual ~ArchHandler_arm(); 33280461Sdim 34280461Sdim const Registry::KindStrings *kindStrings() override { return _sKindStrings; } 35280461Sdim 36280461Sdim Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } 37280461Sdim 38280461Sdim const ArchHandler::StubInfo &stubInfo() override; 39280461Sdim bool isCallSite(const Reference &) override; 40280461Sdim bool isPointer(const Reference &) override; 41280461Sdim bool isPairedReloc(const normalized::Relocation &) override; 42280461Sdim bool isNonCallBranch(const Reference &) override; 43280461Sdim 44280461Sdim bool needsCompactUnwind() override { 45280461Sdim return false; 46280461Sdim } 47280461Sdim Reference::KindValue imageOffsetKind() override { 48280461Sdim return invalid; 49280461Sdim } 50280461Sdim Reference::KindValue imageOffsetKindIndirect() override { 51280461Sdim return invalid; 52280461Sdim } 53280461Sdim 54280461Sdim Reference::KindValue unwindRefToCIEKind() override { 55280461Sdim return invalid; 56280461Sdim } 57280461Sdim 58280461Sdim Reference::KindValue unwindRefToFunctionKind() override { 59280461Sdim return invalid; 60280461Sdim } 61280461Sdim 62280461Sdim Reference::KindValue unwindRefToEhFrameKind() override { 63280461Sdim return invalid; 64280461Sdim } 65280461Sdim 66280461Sdim uint32_t dwarfCompactUnwindType() override { 67280461Sdim // FIXME 68280461Sdim return -1; 69280461Sdim } 70280461Sdim 71280461Sdim std::error_code getReferenceInfo(const normalized::Relocation &reloc, 72280461Sdim const DefinedAtom *inAtom, 73280461Sdim uint32_t offsetInAtom, 74280461Sdim uint64_t fixupAddress, bool swap, 75280461Sdim FindAtomBySectionAndAddress atomFromAddress, 76280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 77280461Sdim Reference::KindValue *kind, 78280461Sdim const lld::Atom **target, 79280461Sdim Reference::Addend *addend) override; 80280461Sdim std::error_code 81280461Sdim getPairReferenceInfo(const normalized::Relocation &reloc1, 82280461Sdim const normalized::Relocation &reloc2, 83280461Sdim const DefinedAtom *inAtom, 84280461Sdim uint32_t offsetInAtom, 85280461Sdim uint64_t fixupAddress, bool swap, bool scatterable, 86280461Sdim FindAtomBySectionAndAddress atomFromAddress, 87280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 88280461Sdim Reference::KindValue *kind, 89280461Sdim const lld::Atom **target, 90280461Sdim Reference::Addend *addend) override; 91280461Sdim 92280461Sdim void generateAtomContent(const DefinedAtom &atom, bool relocatable, 93280461Sdim FindAddressForAtom findAddress, 94280461Sdim FindAddressForAtom findSectionAddress, 95280461Sdim uint64_t imageBaseAddress, 96280461Sdim uint8_t *atomContentBuffer) override; 97280461Sdim 98280461Sdim void appendSectionRelocations(const DefinedAtom &atom, 99280461Sdim uint64_t atomSectionOffset, 100280461Sdim const Reference &ref, 101280461Sdim FindSymbolIndexForAtom, 102280461Sdim FindSectionIndexForAtom, 103280461Sdim FindAddressForAtom, 104280461Sdim normalized::Relocations &) override; 105280461Sdim 106280461Sdim void addAdditionalReferences(MachODefinedAtom &atom) override; 107280461Sdim 108280461Sdim bool isDataInCodeTransition(Reference::KindValue refKind) override { 109280461Sdim switch (refKind) { 110280461Sdim case modeThumbCode: 111280461Sdim case modeArmCode: 112280461Sdim case modeData: 113280461Sdim return true; 114280461Sdim default: 115280461Sdim return false; 116280461Sdim break; 117280461Sdim } 118280461Sdim } 119280461Sdim 120280461Sdim Reference::KindValue dataInCodeTransitionStart( 121280461Sdim const MachODefinedAtom &atom) override { 122280461Sdim return modeData; 123280461Sdim } 124280461Sdim 125280461Sdim Reference::KindValue dataInCodeTransitionEnd( 126280461Sdim const MachODefinedAtom &atom) override { 127280461Sdim return atom.isThumb() ? modeThumbCode : modeArmCode; 128280461Sdim } 129280461Sdim 130280461Sdim bool isThumbFunction(const DefinedAtom &atom) override; 131280461Sdim const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, 132280461Sdim const DefinedAtom &) override; 133280461Sdim 134280461Sdimprivate: 135280461Sdim friend class Thumb2ToArmShimAtom; 136280461Sdim friend class ArmToThumbShimAtom; 137280461Sdim 138280461Sdim static const Registry::KindStrings _sKindStrings[]; 139280461Sdim static const StubInfo _sStubInfoArmPIC; 140280461Sdim 141280461Sdim enum ArmKind : Reference::KindValue { 142280461Sdim invalid, /// for error condition 143280461Sdim 144280461Sdim modeThumbCode, /// Content starting at this offset is thumb. 145280461Sdim modeArmCode, /// Content starting at this offset is arm. 146280461Sdim modeData, /// Content starting at this offset is data. 147280461Sdim 148280461Sdim // Kinds found in mach-o .o files: 149280461Sdim thumb_bl22, /// ex: bl _foo 150280461Sdim thumb_b22, /// ex: b _foo 151280461Sdim thumb_movw, /// ex: movw r1, :lower16:_foo 152280461Sdim thumb_movt, /// ex: movt r1, :lower16:_foo 153280461Sdim thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) 154280461Sdim thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) 155280461Sdim arm_bl24, /// ex: bl _foo 156280461Sdim arm_b24, /// ex: b _foo 157280461Sdim arm_movw, /// ex: movw r1, :lower16:_foo 158280461Sdim arm_movt, /// ex: movt r1, :lower16:_foo 159280461Sdim arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) 160280461Sdim arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) 161280461Sdim pointer32, /// ex: .long _foo 162280461Sdim delta32, /// ex: .long _foo - . 163280461Sdim 164280461Sdim // Kinds introduced by Passes: 165280461Sdim lazyPointer, /// Location contains a lazy pointer. 166280461Sdim lazyImmediateLocation, /// Location contains immediate value used in stub. 167280461Sdim }; 168280461Sdim 169280461Sdim // Utility functions for inspecting/updating instructions. 170280461Sdim static bool isThumbMovw(uint32_t instruction); 171280461Sdim static bool isThumbMovt(uint32_t instruction); 172280461Sdim static bool isArmMovw(uint32_t instruction); 173280461Sdim static bool isArmMovt(uint32_t instruction); 174280461Sdim static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); 175280461Sdim static int32_t getDisplacementFromArmBranch(uint32_t instruction); 176280461Sdim static uint16_t getWordFromThumbMov(uint32_t instruction); 177280461Sdim static uint16_t getWordFromArmMov(uint32_t instruction); 178280461Sdim static uint32_t clearThumbBit(uint32_t value, const Atom *target); 179280461Sdim static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, 180280461Sdim bool targetIsThumb); 181280461Sdim static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, 182280461Sdim int32_t disp, bool targetThumb); 183280461Sdim static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); 184280461Sdim static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); 185280461Sdim 186280461Sdim StringRef stubName(const DefinedAtom &); 187280461Sdim bool useExternalRelocationTo(const Atom &target); 188280461Sdim 189280461Sdim void applyFixupFinal(const Reference &ref, uint8_t *location, 190280461Sdim uint64_t fixupAddress, uint64_t targetAddress, 191280461Sdim uint64_t inAtomAddress, bool &thumbMode, 192280461Sdim bool targetIsThumb); 193280461Sdim 194280461Sdim void applyFixupRelocatable(const Reference &ref, uint8_t *location, 195280461Sdim uint64_t fixupAddress, 196280461Sdim uint64_t targetAddress, 197280461Sdim uint64_t inAtomAddress, bool &thumbMode, 198280461Sdim bool targetIsThumb); 199280461Sdim}; 200280461Sdim 201280461Sdim//===----------------------------------------------------------------------===// 202280461Sdim// ArchHandler_arm 203280461Sdim//===----------------------------------------------------------------------===// 204280461Sdim 205280461SdimArchHandler_arm::ArchHandler_arm() { } 206280461Sdim 207280461SdimArchHandler_arm::~ArchHandler_arm() { } 208280461Sdim 209280461Sdimconst Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { 210280461Sdim LLD_KIND_STRING_ENTRY(invalid), 211280461Sdim LLD_KIND_STRING_ENTRY(modeThumbCode), 212280461Sdim LLD_KIND_STRING_ENTRY(modeArmCode), 213280461Sdim LLD_KIND_STRING_ENTRY(modeData), 214280461Sdim LLD_KIND_STRING_ENTRY(thumb_bl22), 215280461Sdim LLD_KIND_STRING_ENTRY(thumb_b22), 216280461Sdim LLD_KIND_STRING_ENTRY(thumb_movw), 217280461Sdim LLD_KIND_STRING_ENTRY(thumb_movt), 218280461Sdim LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), 219280461Sdim LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), 220280461Sdim LLD_KIND_STRING_ENTRY(arm_bl24), 221280461Sdim LLD_KIND_STRING_ENTRY(arm_b24), 222280461Sdim LLD_KIND_STRING_ENTRY(arm_movw), 223280461Sdim LLD_KIND_STRING_ENTRY(arm_movt), 224280461Sdim LLD_KIND_STRING_ENTRY(arm_movw_funcRel), 225280461Sdim LLD_KIND_STRING_ENTRY(arm_movt_funcRel), 226280461Sdim LLD_KIND_STRING_ENTRY(pointer32), 227280461Sdim LLD_KIND_STRING_ENTRY(delta32), 228280461Sdim LLD_KIND_STRING_ENTRY(lazyPointer), 229280461Sdim LLD_KIND_STRING_ENTRY(lazyImmediateLocation), 230280461Sdim LLD_KIND_STRING_END 231280461Sdim}; 232280461Sdim 233280461Sdimconst ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { 234280461Sdim "dyld_stub_binder", 235280461Sdim 236280461Sdim // References in lazy pointer 237280461Sdim { Reference::KindArch::ARM, pointer32, 0, 0 }, 238280461Sdim { Reference::KindArch::ARM, lazyPointer, 0, 0 }, 239280461Sdim 240280461Sdim // GOT pointer to dyld_stub_binder 241280461Sdim { Reference::KindArch::ARM, pointer32, 0, 0 }, 242280461Sdim 243280461Sdim // arm code alignment 2^2 244280461Sdim 2, 245280461Sdim 246280461Sdim // Stub size and code 247280461Sdim 16, 248280461Sdim { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 249280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 250280461Sdim 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 251280461Sdim 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) 252280461Sdim { Reference::KindArch::ARM, delta32, 12, 0 }, 253280461Sdim { false, 0, 0, 0 }, 254280461Sdim 255280461Sdim // Stub Helper size and code 256280461Sdim 12, 257280461Sdim { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] 258280461Sdim 0x00, 0x00, 0x00, 0xEA, // b _helperhelper 259280461Sdim 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset 260280461Sdim { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, 261280461Sdim { Reference::KindArch::ARM, arm_b24, 4, 0 }, 262280461Sdim 263280461Sdim // Stub Helper-Common size and code 264280461Sdim 36, 265280461Sdim { // push lazy-info-offset 266280461Sdim 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! 267280461Sdim // push address of dyld_mageLoaderCache 268280461Sdim 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 269280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 270280461Sdim 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! 271280461Sdim // jump through dyld_stub_binder 272280461Sdim 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 273280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 274280461Sdim 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 275280461Sdim 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) 276280461Sdim 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) 277280461Sdim { Reference::KindArch::ARM, delta32, 28, 0xC }, 278280461Sdim { false, 0, 0, 0 }, 279280461Sdim { Reference::KindArch::ARM, delta32, 32, 0x04 }, 280280461Sdim { false, 0, 0, 0 } 281280461Sdim}; 282280461Sdim 283280461Sdimconst ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { 284280461Sdim // If multiple kinds of stubs are supported, select which StubInfo here. 285280461Sdim return _sStubInfoArmPIC; 286280461Sdim} 287280461Sdim 288280461Sdimbool ArchHandler_arm::isCallSite(const Reference &ref) { 289280461Sdim switch (ref.kindValue()) { 290280461Sdim case thumb_b22: 291280461Sdim case thumb_bl22: 292280461Sdim case arm_b24: 293280461Sdim case arm_bl24: 294280461Sdim return true; 295280461Sdim default: 296280461Sdim return false; 297280461Sdim } 298280461Sdim} 299280461Sdim 300280461Sdimbool ArchHandler_arm::isPointer(const Reference &ref) { 301280461Sdim return (ref.kindValue() == pointer32); 302280461Sdim} 303280461Sdim 304280461Sdimbool ArchHandler_arm::isNonCallBranch(const Reference &ref) { 305280461Sdim switch (ref.kindValue()) { 306280461Sdim case thumb_b22: 307280461Sdim case arm_b24: 308280461Sdim return true; 309280461Sdim default: 310280461Sdim return false; 311280461Sdim } 312280461Sdim} 313280461Sdim 314280461Sdimbool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { 315280461Sdim switch (reloc.type) { 316280461Sdim case ARM_RELOC_SECTDIFF: 317280461Sdim case ARM_RELOC_LOCAL_SECTDIFF: 318280461Sdim case ARM_RELOC_HALF_SECTDIFF: 319280461Sdim case ARM_RELOC_HALF: 320280461Sdim return true; 321280461Sdim default: 322280461Sdim return false; 323280461Sdim } 324280461Sdim} 325280461Sdim 326280461Sdim/// Trace references from stub atom to lazy pointer to target and get its name. 327280461SdimStringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { 328280461Sdim assert(stubAtom.contentType() == DefinedAtom::typeStub); 329280461Sdim for (const Reference *ref : stubAtom) { 330280461Sdim if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) { 331280461Sdim if (lp->contentType() != DefinedAtom::typeLazyPointer) 332280461Sdim continue; 333280461Sdim for (const Reference *ref2 : *lp) { 334280461Sdim if (ref2->kindValue() != lazyPointer) 335280461Sdim continue; 336280461Sdim return ref2->target()->name(); 337280461Sdim } 338280461Sdim } 339280461Sdim } 340280461Sdim return "stub"; 341280461Sdim} 342280461Sdim 343280461Sdim/// Extract displacement from an ARM b/bl/blx instruction. 344280461Sdimint32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { 345280461Sdim // Sign-extend imm24 346280461Sdim int32_t displacement = (instruction & 0x00FFFFFF) << 2; 347280461Sdim if ((displacement & 0x02000000) != 0) 348280461Sdim displacement |= 0xFC000000; 349280461Sdim // If this is BLX and H bit set, add 2. 350280461Sdim if ((instruction & 0xFF000000) == 0xFB000000) 351280461Sdim displacement += 2; 352280461Sdim return displacement; 353280461Sdim} 354280461Sdim 355280461Sdim/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. 356280461Sdimuint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, 357280461Sdim int32_t displacement, 358280461Sdim bool targetIsThumb) { 359280461Sdim assert((displacement <= 33554428) && (displacement > (-33554432)) 360280461Sdim && "arm branch out of range"); 361280461Sdim bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); 362280461Sdim uint32_t newInstruction = (instruction & 0xFF000000); 363280461Sdim uint32_t h = 0; 364280461Sdim if (targetIsThumb) { 365280461Sdim // Force use of BLX. 366280461Sdim newInstruction = 0xFA000000; 367280461Sdim if (!is_blx) { 368280461Sdim assert(((instruction & 0xF0000000) == 0xE0000000) 369280461Sdim && "no conditional arm blx"); 370280461Sdim assert(((instruction & 0xFF000000) == 0xEB000000) 371280461Sdim && "no arm pc-rel BX instruction"); 372280461Sdim } 373280461Sdim if (displacement & 2) 374280461Sdim h = 1; 375280461Sdim } 376280461Sdim else { 377280461Sdim // Force use of B/BL. 378280461Sdim if (is_blx) 379280461Sdim newInstruction = 0xEB000000; 380280461Sdim } 381280461Sdim newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); 382280461Sdim return newInstruction; 383280461Sdim} 384280461Sdim 385280461Sdim/// Extract displacement from a thumb b/bl/blx instruction. 386280461Sdimint32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, 387280461Sdim uint32_t instrAddr) { 388280461Sdim bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); 389280461Sdim uint32_t s = (instruction >> 10) & 0x1; 390280461Sdim uint32_t j1 = (instruction >> 29) & 0x1; 391280461Sdim uint32_t j2 = (instruction >> 27) & 0x1; 392280461Sdim uint32_t imm10 = instruction & 0x3FF; 393280461Sdim uint32_t imm11 = (instruction >> 16) & 0x7FF; 394280461Sdim uint32_t i1 = (j1 == s); 395280461Sdim uint32_t i2 = (j2 == s); 396280461Sdim uint32_t dis = 397280461Sdim (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); 398280461Sdim int32_t sdis = dis; 399280461Sdim int32_t result = s ? (sdis | 0xFE000000) : sdis; 400280461Sdim if (is_blx && (instrAddr & 0x2)) { 401280461Sdim // The thumb blx instruction always has low bit of imm11 as zero. The way 402280461Sdim // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that 403280461Sdim // the blx instruction always 4-byte aligns the pc before adding the 404280461Sdim // displacement from the blx. We must emulate that when decoding this. 405280461Sdim result -= 2; 406280461Sdim } 407280461Sdim return result; 408280461Sdim} 409280461Sdim 410280461Sdim/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. 411280461Sdimuint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, 412280461Sdim uint32_t instrAddr, 413280461Sdim int32_t displacement, 414280461Sdim bool targetIsThumb) { 415280461Sdim assert((displacement <= 16777214) && (displacement > (-16777216)) 416280461Sdim && "thumb branch out of range"); 417280461Sdim bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); 418280461Sdim bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); 419280461Sdim bool is_b = ((instruction & 0xD000F800) == 0x9000F000); 420280461Sdim uint32_t newInstruction = (instruction & 0xD000F800); 421280461Sdim if (is_bl || is_blx) { 422280461Sdim if (targetIsThumb) { 423280461Sdim newInstruction = 0xD000F000; // Use bl 424280461Sdim } else { 425280461Sdim newInstruction = 0xC000F000; // Use blx 426280461Sdim // See note in getDisplacementFromThumbBranch() about blx. 427280461Sdim if (instrAddr & 0x2) 428280461Sdim displacement += 2; 429280461Sdim } 430280461Sdim } else if (is_b) { 431280461Sdim assert(targetIsThumb && "no pc-rel thumb branch instruction that " 432280461Sdim "switches to arm mode"); 433280461Sdim } 434280461Sdim else { 435280461Sdim llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); 436280461Sdim } 437280461Sdim uint32_t s = (uint32_t)(displacement >> 24) & 0x1; 438280461Sdim uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; 439280461Sdim uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; 440280461Sdim uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; 441280461Sdim uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; 442280461Sdim uint32_t j1 = (i1 == s); 443280461Sdim uint32_t j2 = (i2 == s); 444280461Sdim uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; 445280461Sdim uint32_t firstDisp = (s << 10) | imm10; 446280461Sdim newInstruction |= (nextDisp << 16) | firstDisp; 447280461Sdim return newInstruction; 448280461Sdim} 449280461Sdim 450280461Sdimbool ArchHandler_arm::isThumbMovw(uint32_t instruction) { 451280461Sdim return (instruction & 0x8000FBF0) == 0x0000F240; 452280461Sdim} 453280461Sdim 454280461Sdimbool ArchHandler_arm::isThumbMovt(uint32_t instruction) { 455280461Sdim return (instruction & 0x8000FBF0) == 0x0000F2C0; 456280461Sdim} 457280461Sdim 458280461Sdimbool ArchHandler_arm::isArmMovw(uint32_t instruction) { 459280461Sdim return (instruction & 0x0FF00000) == 0x03000000; 460280461Sdim} 461280461Sdim 462280461Sdimbool ArchHandler_arm::isArmMovt(uint32_t instruction) { 463280461Sdim return (instruction & 0x0FF00000) == 0x03400000; 464280461Sdim} 465280461Sdim 466280461Sdim 467280461Sdimuint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { 468280461Sdim assert(isThumbMovw(instruction) || isThumbMovt(instruction)); 469280461Sdim uint32_t i = ((instruction & 0x00000400) >> 10); 470280461Sdim uint32_t imm4 = (instruction & 0x0000000F); 471280461Sdim uint32_t imm3 = ((instruction & 0x70000000) >> 28); 472280461Sdim uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); 473280461Sdim return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; 474280461Sdim} 475280461Sdim 476280461Sdimuint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { 477280461Sdim assert(isArmMovw(instruction) || isArmMovt(instruction)); 478280461Sdim uint32_t imm4 = ((instruction & 0x000F0000) >> 16); 479280461Sdim uint32_t imm12 = (instruction & 0x00000FFF); 480280461Sdim return (imm4 << 12) | imm12; 481280461Sdim} 482280461Sdim 483280461Sdim 484280461Sdimuint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { 485280461Sdim assert(isThumbMovw(instr) || isThumbMovt(instr)); 486280461Sdim uint32_t imm4 = (word & 0xF000) >> 12; 487280461Sdim uint32_t i = (word & 0x0800) >> 11; 488280461Sdim uint32_t imm3 = (word & 0x0700) >> 8; 489280461Sdim uint32_t imm8 = word & 0x00FF; 490280461Sdim return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); 491280461Sdim} 492280461Sdim 493280461Sdimuint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { 494280461Sdim assert(isArmMovw(instr) || isArmMovt(instr)); 495280461Sdim uint32_t imm4 = (word & 0xF000) >> 12; 496280461Sdim uint32_t imm12 = word & 0x0FFF; 497280461Sdim return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; 498280461Sdim} 499280461Sdim 500280461Sdim 501280461Sdimuint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { 502280461Sdim // The assembler often adds one to the address of a thumb function. 503280461Sdim // We need to undo that so it does not look like an addend. 504280461Sdim if (value & 1) { 505280461Sdim if (isa<DefinedAtom>(target)) { 506280461Sdim const MachODefinedAtom *machoTarget = 507280461Sdim reinterpret_cast<const MachODefinedAtom *>(target); 508280461Sdim if (machoTarget->isThumb()) 509280461Sdim value &= -2; // mask off thumb-bit 510280461Sdim } 511280461Sdim } 512280461Sdim return value; 513280461Sdim} 514280461Sdim 515280461Sdimstd::error_code ArchHandler_arm::getReferenceInfo( 516280461Sdim const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, 517280461Sdim uint64_t fixupAddress, bool isBig, 518280461Sdim FindAtomBySectionAndAddress atomFromAddress, 519280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, 520280461Sdim const lld::Atom **target, Reference::Addend *addend) { 521280461Sdim typedef std::error_code E; 522280461Sdim const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; 523280461Sdim uint64_t targetAddress; 524280461Sdim uint32_t instruction = *(const ulittle32_t *)fixupContent; 525280461Sdim int32_t displacement; 526280461Sdim switch (relocPattern(reloc)) { 527280461Sdim case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: 528280461Sdim // ex: bl _foo (and _foo is undefined) 529280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 530280461Sdim *kind = thumb_b22; 531280461Sdim else 532280461Sdim *kind = thumb_bl22; 533280461Sdim if (E ec = atomFromSymbolIndex(reloc.symbol, target)) 534280461Sdim return ec; 535280461Sdim // Instruction contains branch to addend. 536280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 537280461Sdim *addend = fixupAddress + 4 + displacement; 538280461Sdim return std::error_code(); 539280461Sdim case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: 540280461Sdim // ex: bl _foo (and _foo is defined) 541280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 542280461Sdim *kind = thumb_b22; 543280461Sdim else 544280461Sdim *kind = thumb_bl22; 545280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 546280461Sdim targetAddress = fixupAddress + 4 + displacement; 547280461Sdim return atomFromAddress(reloc.symbol, targetAddress, target, addend); 548280461Sdim case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: 549280461Sdim // ex: bl _foo+4 (and _foo is defined) 550280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 551280461Sdim *kind = thumb_b22; 552280461Sdim else 553280461Sdim *kind = thumb_bl22; 554280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 555280461Sdim targetAddress = fixupAddress + 4 + displacement; 556280461Sdim if (E ec = atomFromAddress(0, reloc.value, target, addend)) 557280461Sdim return ec; 558280461Sdim // reloc.value is target atom's address. Instruction contains branch 559280461Sdim // to atom+addend. 560280461Sdim *addend += (targetAddress - reloc.value); 561280461Sdim return std::error_code(); 562280461Sdim case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: 563280461Sdim // ex: bl _foo (and _foo is undefined) 564280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 565280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 566280461Sdim *kind = arm_b24; 567280461Sdim else 568280461Sdim *kind = arm_bl24; 569280461Sdim if (E ec = atomFromSymbolIndex(reloc.symbol, target)) 570280461Sdim return ec; 571280461Sdim // Instruction contains branch to addend. 572280461Sdim displacement = getDisplacementFromArmBranch(instruction); 573280461Sdim *addend = fixupAddress + 8 + displacement; 574280461Sdim return std::error_code(); 575280461Sdim case ARM_RELOC_BR24 | rPcRel | rLength4: 576280461Sdim // ex: bl _foo (and _foo is defined) 577280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 578280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 579280461Sdim *kind = arm_b24; 580280461Sdim else 581280461Sdim *kind = arm_bl24; 582280461Sdim displacement = getDisplacementFromArmBranch(instruction); 583280461Sdim targetAddress = fixupAddress + 8 + displacement; 584280461Sdim return atomFromAddress(reloc.symbol, targetAddress, target, addend); 585280461Sdim case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: 586280461Sdim // ex: bl _foo+4 (and _foo is defined) 587280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 588280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 589280461Sdim *kind = arm_b24; 590280461Sdim else 591280461Sdim *kind = arm_bl24; 592280461Sdim displacement = getDisplacementFromArmBranch(instruction); 593280461Sdim targetAddress = fixupAddress + 8 + displacement; 594280461Sdim if (E ec = atomFromAddress(0, reloc.value, target, addend)) 595280461Sdim return ec; 596280461Sdim // reloc.value is target atom's address. Instruction contains branch 597280461Sdim // to atom+addend. 598280461Sdim *addend += (targetAddress - reloc.value); 599280461Sdim return std::error_code(); 600280461Sdim case ARM_RELOC_VANILLA | rExtern | rLength4: 601280461Sdim // ex: .long _foo (and _foo is undefined) 602280461Sdim *kind = pointer32; 603280461Sdim if (E ec = atomFromSymbolIndex(reloc.symbol, target)) 604280461Sdim return ec; 605280461Sdim *addend = instruction; 606280461Sdim return std::error_code(); 607280461Sdim case ARM_RELOC_VANILLA | rLength4: 608280461Sdim // ex: .long _foo (and _foo is defined) 609280461Sdim *kind = pointer32; 610280461Sdim if (E ec = atomFromAddress(reloc.symbol, instruction, target, addend)) 611280461Sdim return ec; 612280461Sdim *addend = clearThumbBit((uint32_t) * addend, *target); 613280461Sdim return std::error_code(); 614280461Sdim case ARM_RELOC_VANILLA | rScattered | rLength4: 615280461Sdim // ex: .long _foo+a (and _foo is defined) 616280461Sdim *kind = pointer32; 617280461Sdim if (E ec = atomFromAddress(0, reloc.value, target, addend)) 618280461Sdim return ec; 619280461Sdim *addend += (clearThumbBit(instruction, *target) - reloc.value); 620280461Sdim return std::error_code(); 621280461Sdim default: 622280461Sdim return make_dynamic_error_code(Twine("unsupported arm relocation type")); 623280461Sdim } 624280461Sdim return std::error_code(); 625280461Sdim} 626280461Sdim 627280461Sdimstd::error_code 628280461SdimArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, 629280461Sdim const normalized::Relocation &reloc2, 630280461Sdim const DefinedAtom *inAtom, 631280461Sdim uint32_t offsetInAtom, 632280461Sdim uint64_t fixupAddress, bool isBig, 633280461Sdim bool scatterable, 634280461Sdim FindAtomBySectionAndAddress atomFromAddr, 635280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 636280461Sdim Reference::KindValue *kind, 637280461Sdim const lld::Atom **target, 638280461Sdim Reference::Addend *addend) { 639280461Sdim bool pointerDiff = false; 640280461Sdim bool funcRel; 641280461Sdim bool top; 642280461Sdim bool thumbReloc; 643280461Sdim switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { 644280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | 645280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbLo): 646280461Sdim // ex: movw r1, :lower16:(_x-L1) [thumb mode] 647280461Sdim *kind = thumb_movw_funcRel; 648280461Sdim funcRel = true; 649280461Sdim top = false; 650280461Sdim thumbReloc = true; 651280461Sdim break; 652280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | 653280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbHi): 654280461Sdim // ex: movt r1, :upper16:(_x-L1) [thumb mode] 655280461Sdim *kind = thumb_movt_funcRel; 656280461Sdim funcRel = true; 657280461Sdim top = true; 658280461Sdim thumbReloc = true; 659280461Sdim break; 660280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | 661280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmLo): 662280461Sdim // ex: movw r1, :lower16:(_x-L1) [arm mode] 663280461Sdim *kind = arm_movw_funcRel; 664280461Sdim funcRel = true; 665280461Sdim top = false; 666280461Sdim thumbReloc = false; 667280461Sdim break; 668280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | 669280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmHi): 670280461Sdim // ex: movt r1, :upper16:(_x-L1) [arm mode] 671280461Sdim *kind = arm_movt_funcRel; 672280461Sdim funcRel = true; 673280461Sdim top = true; 674280461Sdim thumbReloc = false; 675280461Sdim break; 676280461Sdim case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | 677280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 678280461Sdim // ex: movw r1, :lower16:_x [thumb mode] 679280461Sdim *kind = thumb_movw; 680280461Sdim funcRel = false; 681280461Sdim top = false; 682280461Sdim thumbReloc = true; 683280461Sdim break; 684280461Sdim case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | 685280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 686280461Sdim // ex: movt r1, :upper16:_x [thumb mode] 687280461Sdim *kind = thumb_movt; 688280461Sdim funcRel = false; 689280461Sdim top = true; 690280461Sdim thumbReloc = true; 691280461Sdim break; 692280461Sdim case ((ARM_RELOC_HALF | rLenArmLo) << 16 | 693280461Sdim ARM_RELOC_PAIR | rLenArmLo): 694280461Sdim // ex: movw r1, :lower16:_x [arm mode] 695280461Sdim *kind = arm_movw; 696280461Sdim funcRel = false; 697280461Sdim top = false; 698280461Sdim thumbReloc = false; 699280461Sdim break; 700280461Sdim case ((ARM_RELOC_HALF | rLenArmHi) << 16 | 701280461Sdim ARM_RELOC_PAIR | rLenArmHi): 702280461Sdim // ex: movt r1, :upper16:_x [arm mode] 703280461Sdim *kind = arm_movt; 704280461Sdim funcRel = false; 705280461Sdim top = true; 706280461Sdim thumbReloc = false; 707280461Sdim break; 708280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | 709280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 710280461Sdim // ex: movw r1, :lower16:_x+a [thumb mode] 711280461Sdim *kind = thumb_movw; 712280461Sdim funcRel = false; 713280461Sdim top = false; 714280461Sdim thumbReloc = true; 715280461Sdim break; 716280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | 717280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 718280461Sdim // ex: movt r1, :upper16:_x+a [thumb mode] 719280461Sdim *kind = thumb_movt; 720280461Sdim funcRel = false; 721280461Sdim top = true; 722280461Sdim thumbReloc = true; 723280461Sdim break; 724280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | 725280461Sdim ARM_RELOC_PAIR | rLenArmLo): 726280461Sdim // ex: movw r1, :lower16:_x+a [arm mode] 727280461Sdim *kind = arm_movw; 728280461Sdim funcRel = false; 729280461Sdim top = false; 730280461Sdim thumbReloc = false; 731280461Sdim break; 732280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | 733280461Sdim ARM_RELOC_PAIR | rLenArmHi): 734280461Sdim // ex: movt r1, :upper16:_x+a [arm mode] 735280461Sdim *kind = arm_movt; 736280461Sdim funcRel = false; 737280461Sdim top = true; 738280461Sdim thumbReloc = false; 739280461Sdim break; 740280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | 741280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 742280461Sdim // ex: movw r1, :lower16:_undef [thumb mode] 743280461Sdim *kind = thumb_movw; 744280461Sdim funcRel = false; 745280461Sdim top = false; 746280461Sdim thumbReloc = true; 747280461Sdim break; 748280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | 749280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 750280461Sdim // ex: movt r1, :upper16:_undef [thumb mode] 751280461Sdim *kind = thumb_movt; 752280461Sdim funcRel = false; 753280461Sdim top = true; 754280461Sdim thumbReloc = true; 755280461Sdim break; 756280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | 757280461Sdim ARM_RELOC_PAIR | rLenArmLo): 758280461Sdim // ex: movw r1, :lower16:_undef [arm mode] 759280461Sdim *kind = arm_movw; 760280461Sdim funcRel = false; 761280461Sdim top = false; 762280461Sdim thumbReloc = false; 763280461Sdim break; 764280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | 765280461Sdim ARM_RELOC_PAIR | rLenArmHi): 766280461Sdim // ex: movt r1, :upper16:_undef [arm mode] 767280461Sdim *kind = arm_movt; 768280461Sdim funcRel = false; 769280461Sdim top = true; 770280461Sdim thumbReloc = false; 771280461Sdim break; 772280461Sdim case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | 773280461Sdim ARM_RELOC_PAIR | rScattered | rLength4): 774280461Sdim case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | 775280461Sdim ARM_RELOC_PAIR | rScattered | rLength4): 776280461Sdim // ex: .long _foo - . 777280461Sdim pointerDiff = true; 778280461Sdim break; 779280461Sdim default: 780280461Sdim return make_dynamic_error_code(Twine("unsupported arm relocation pair")); 781280461Sdim } 782280461Sdim const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; 783280461Sdim std::error_code ec; 784280461Sdim uint32_t instruction = *(const ulittle32_t *)fixupContent; 785280461Sdim uint32_t value; 786280461Sdim uint32_t fromAddress; 787280461Sdim uint32_t toAddress; 788280461Sdim uint16_t instruction16; 789280461Sdim uint16_t other16; 790280461Sdim const lld::Atom *fromTarget; 791280461Sdim Reference::Addend offsetInTo; 792280461Sdim Reference::Addend offsetInFrom; 793280461Sdim if (pointerDiff) { 794280461Sdim toAddress = reloc1.value; 795280461Sdim fromAddress = reloc2.value; 796280461Sdim ec = atomFromAddr(0, toAddress, target, &offsetInTo); 797280461Sdim if (ec) 798280461Sdim return ec; 799280461Sdim ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); 800280461Sdim if (ec) 801280461Sdim return ec; 802280461Sdim if (scatterable && (fromTarget != inAtom)) 803280461Sdim return make_dynamic_error_code(Twine("SECTDIFF relocation where " 804280461Sdim "subtrahend label is not in atom")); 805280461Sdim *kind = delta32; 806280461Sdim value = clearThumbBit(instruction, *target); 807280461Sdim *addend = (int32_t)(value - (toAddress - fixupAddress)); 808280461Sdim } else if (funcRel) { 809280461Sdim toAddress = reloc1.value; 810280461Sdim fromAddress = reloc2.value; 811280461Sdim ec = atomFromAddr(0, toAddress, target, &offsetInTo); 812280461Sdim if (ec) 813280461Sdim return ec; 814280461Sdim ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom); 815280461Sdim if (ec) 816280461Sdim return ec; 817280461Sdim if (fromTarget != inAtom) 818280461Sdim return make_dynamic_error_code( 819280461Sdim Twine("ARM_RELOC_HALF_SECTDIFF relocation " 820280461Sdim "where subtrahend label is not in atom")); 821280461Sdim other16 = (reloc2.offset & 0xFFFF); 822280461Sdim if (thumbReloc) { 823280461Sdim if (top) { 824280461Sdim if (!isThumbMovt(instruction)) 825280461Sdim return make_dynamic_error_code(Twine("expected movt instruction")); 826280461Sdim } 827280461Sdim else { 828280461Sdim if (!isThumbMovw(instruction)) 829280461Sdim return make_dynamic_error_code(Twine("expected movw instruction")); 830280461Sdim } 831280461Sdim instruction16 = getWordFromThumbMov(instruction); 832280461Sdim } 833280461Sdim else { 834280461Sdim if (top) { 835280461Sdim if (!isArmMovt(instruction)) 836280461Sdim return make_dynamic_error_code(Twine("expected movt instruction")); 837280461Sdim } 838280461Sdim else { 839280461Sdim if (!isArmMovw(instruction)) 840280461Sdim return make_dynamic_error_code(Twine("expected movw instruction")); 841280461Sdim } 842280461Sdim instruction16 = getWordFromArmMov(instruction); 843280461Sdim } 844280461Sdim if (top) 845280461Sdim value = (instruction16 << 16) | other16; 846280461Sdim else 847280461Sdim value = (other16 << 16) | instruction16; 848280461Sdim value = clearThumbBit(value, *target); 849280461Sdim int64_t ta = (int64_t) value - (toAddress - fromAddress); 850280461Sdim *addend = ta - offsetInFrom; 851280461Sdim return std::error_code(); 852280461Sdim } else { 853280461Sdim uint32_t sectIndex; 854280461Sdim if (thumbReloc) { 855280461Sdim if (top) { 856280461Sdim if (!isThumbMovt(instruction)) 857280461Sdim return make_dynamic_error_code(Twine("expected movt instruction")); 858280461Sdim } 859280461Sdim else { 860280461Sdim if (!isThumbMovw(instruction)) 861280461Sdim return make_dynamic_error_code(Twine("expected movw instruction")); 862280461Sdim } 863280461Sdim instruction16 = getWordFromThumbMov(instruction); 864280461Sdim } 865280461Sdim else { 866280461Sdim if (top) { 867280461Sdim if (!isArmMovt(instruction)) 868280461Sdim return make_dynamic_error_code(Twine("expected movt instruction")); 869280461Sdim } 870280461Sdim else { 871280461Sdim if (!isArmMovw(instruction)) 872280461Sdim return make_dynamic_error_code(Twine("expected movw instruction")); 873280461Sdim } 874280461Sdim instruction16 = getWordFromArmMov(instruction); 875280461Sdim } 876280461Sdim other16 = (reloc2.offset & 0xFFFF); 877280461Sdim if (top) 878280461Sdim value = (instruction16 << 16) | other16; 879280461Sdim else 880280461Sdim value = (other16 << 16) | instruction16; 881280461Sdim if (reloc1.isExtern) { 882280461Sdim ec = atomFromSymbolIndex(reloc1.symbol, target); 883280461Sdim if (ec) 884280461Sdim return ec; 885280461Sdim *addend = value; 886280461Sdim } else { 887280461Sdim if (reloc1.scattered) { 888280461Sdim toAddress = reloc1.value; 889280461Sdim sectIndex = 0; 890280461Sdim } else { 891280461Sdim toAddress = value; 892280461Sdim sectIndex = reloc1.symbol; 893280461Sdim } 894280461Sdim ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo); 895280461Sdim if (ec) 896280461Sdim return ec; 897280461Sdim *addend = value - toAddress; 898280461Sdim } 899280461Sdim } 900280461Sdim 901280461Sdim return std::error_code(); 902280461Sdim} 903280461Sdim 904280461Sdimvoid ArchHandler_arm::applyFixupFinal(const Reference &ref, uint8_t *loc, 905280461Sdim uint64_t fixupAddress, 906280461Sdim uint64_t targetAddress, 907280461Sdim uint64_t inAtomAddress, 908280461Sdim bool &thumbMode, bool targetIsThumb) { 909280461Sdim if (ref.kindNamespace() != Reference::KindNamespace::mach_o) 910280461Sdim return; 911280461Sdim assert(ref.kindArch() == Reference::KindArch::ARM); 912280461Sdim ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); 913280461Sdim int32_t displacement; 914280461Sdim uint16_t value16; 915280461Sdim uint32_t value32; 916280461Sdim switch (static_cast<ArmKind>(ref.kindValue())) { 917280461Sdim case modeThumbCode: 918280461Sdim thumbMode = true; 919280461Sdim break; 920280461Sdim case modeArmCode: 921280461Sdim thumbMode = false; 922280461Sdim break; 923280461Sdim case modeData: 924280461Sdim break; 925280461Sdim case thumb_b22: 926280461Sdim case thumb_bl22: 927280461Sdim assert(thumbMode); 928280461Sdim displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); 929280461Sdim value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, 930280461Sdim displacement, targetIsThumb); 931280461Sdim *loc32 = value32; 932280461Sdim break; 933280461Sdim case thumb_movw: 934280461Sdim assert(thumbMode); 935280461Sdim value16 = (targetAddress + ref.addend()) & 0xFFFF; 936280461Sdim if (targetIsThumb) 937280461Sdim value16 |= 1; 938280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 939280461Sdim break; 940280461Sdim case thumb_movt: 941280461Sdim assert(thumbMode); 942280461Sdim value16 = (targetAddress + ref.addend()) >> 16; 943280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 944280461Sdim break; 945280461Sdim case thumb_movw_funcRel: 946280461Sdim assert(thumbMode); 947280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; 948280461Sdim if (targetIsThumb) 949280461Sdim value16 |= 1; 950280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 951280461Sdim break; 952280461Sdim case thumb_movt_funcRel: 953280461Sdim assert(thumbMode); 954280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; 955280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 956280461Sdim break; 957280461Sdim case arm_b24: 958280461Sdim case arm_bl24: 959280461Sdim assert(!thumbMode); 960280461Sdim displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); 961280461Sdim value32 = setDisplacementInArmBranch(*loc32, displacement, targetIsThumb); 962280461Sdim *loc32 = value32; 963280461Sdim break; 964280461Sdim case arm_movw: 965280461Sdim assert(!thumbMode); 966280461Sdim value16 = (targetAddress + ref.addend()) & 0xFFFF; 967280461Sdim if (targetIsThumb) 968280461Sdim value16 |= 1; 969280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 970280461Sdim break; 971280461Sdim case arm_movt: 972280461Sdim assert(!thumbMode); 973280461Sdim value16 = (targetAddress + ref.addend()) >> 16; 974280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 975280461Sdim break; 976280461Sdim case arm_movw_funcRel: 977280461Sdim assert(!thumbMode); 978280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; 979280461Sdim if (targetIsThumb) 980280461Sdim value16 |= 1; 981280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 982280461Sdim break; 983280461Sdim case arm_movt_funcRel: 984280461Sdim assert(!thumbMode); 985280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; 986280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 987280461Sdim break; 988280461Sdim case pointer32: 989280461Sdim if (targetIsThumb) 990280461Sdim *loc32 = targetAddress + ref.addend() + 1; 991280461Sdim else 992280461Sdim *loc32 = targetAddress + ref.addend(); 993280461Sdim break; 994280461Sdim case delta32: 995280461Sdim if (targetIsThumb) 996280461Sdim *loc32 = targetAddress - fixupAddress + ref.addend() + 1; 997280461Sdim else 998280461Sdim *loc32 = targetAddress - fixupAddress + ref.addend(); 999280461Sdim break; 1000280461Sdim case lazyPointer: 1001280461Sdim // do nothing 1002280461Sdim break; 1003280461Sdim case lazyImmediateLocation: 1004280461Sdim *loc32 = ref.addend(); 1005280461Sdim break; 1006280461Sdim case invalid: 1007280461Sdim llvm_unreachable("invalid ARM Reference Kind"); 1008280461Sdim break; 1009280461Sdim } 1010280461Sdim} 1011280461Sdim 1012280461Sdimvoid ArchHandler_arm::generateAtomContent(const DefinedAtom &atom, 1013280461Sdim bool relocatable, 1014280461Sdim FindAddressForAtom findAddress, 1015280461Sdim FindAddressForAtom findSectionAddress, 1016280461Sdim uint64_t imageBaseAddress, 1017280461Sdim uint8_t *atomContentBuffer) { 1018280461Sdim // Copy raw bytes. 1019280461Sdim memcpy(atomContentBuffer, atom.rawContent().data(), atom.size()); 1020280461Sdim // Apply fix-ups. 1021280461Sdim bool thumbMode = false; 1022280461Sdim for (const Reference *ref : atom) { 1023280461Sdim uint32_t offset = ref->offsetInAtom(); 1024280461Sdim const Atom *target = ref->target(); 1025280461Sdim uint64_t targetAddress = 0; 1026280461Sdim bool targetIsThumb = false; 1027280461Sdim if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) { 1028280461Sdim targetAddress = findAddress(*target); 1029280461Sdim targetIsThumb = isThumbFunction(*defTarg); 1030280461Sdim } 1031280461Sdim uint64_t atomAddress = findAddress(atom); 1032280461Sdim uint64_t fixupAddress = atomAddress + offset; 1033280461Sdim if (relocatable) { 1034280461Sdim applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, 1035280461Sdim targetAddress, atomAddress, thumbMode, 1036280461Sdim targetIsThumb); 1037280461Sdim } else { 1038280461Sdim applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, 1039280461Sdim targetAddress, atomAddress, thumbMode, targetIsThumb); 1040280461Sdim } 1041280461Sdim } 1042280461Sdim} 1043280461Sdim 1044280461Sdim 1045280461Sdimbool ArchHandler_arm::useExternalRelocationTo(const Atom &target) { 1046280461Sdim // Undefined symbols are referenced via external relocations. 1047280461Sdim if (isa<UndefinedAtom>(&target)) 1048280461Sdim return true; 1049280461Sdim if (const DefinedAtom *defAtom = dyn_cast<DefinedAtom>(&target)) { 1050280461Sdim switch (defAtom->merge()) { 1051280461Sdim case DefinedAtom::mergeAsTentative: 1052280461Sdim // Tentative definitions are referenced via external relocations. 1053280461Sdim return true; 1054280461Sdim case DefinedAtom::mergeAsWeak: 1055280461Sdim case DefinedAtom::mergeAsWeakAndAddressUsed: 1056280461Sdim // Global weak-defs are referenced via external relocations. 1057280461Sdim return (defAtom->scope() == DefinedAtom::scopeGlobal); 1058280461Sdim default: 1059280461Sdim break; 1060280461Sdim } 1061280461Sdim } 1062280461Sdim // Everything else is reference via an internal relocation. 1063280461Sdim return false; 1064280461Sdim} 1065280461Sdim 1066280461Sdimvoid ArchHandler_arm::applyFixupRelocatable(const Reference &ref, uint8_t *loc, 1067280461Sdim uint64_t fixupAddress, 1068280461Sdim uint64_t targetAddress, 1069280461Sdim uint64_t inAtomAddress, 1070280461Sdim bool &thumbMode, 1071280461Sdim bool targetIsThumb) { 1072280461Sdim if (ref.kindNamespace() != Reference::KindNamespace::mach_o) 1073280461Sdim return; 1074280461Sdim assert(ref.kindArch() == Reference::KindArch::ARM); 1075280461Sdim bool useExternalReloc = useExternalRelocationTo(*ref.target()); 1076280461Sdim ulittle32_t *loc32 = reinterpret_cast<ulittle32_t *>(loc); 1077280461Sdim int32_t displacement; 1078280461Sdim uint16_t value16; 1079280461Sdim uint32_t value32; 1080280461Sdim bool targetIsUndef = isa<UndefinedAtom>(ref.target()); 1081280461Sdim switch (static_cast<ArmKind>(ref.kindValue())) { 1082280461Sdim case modeThumbCode: 1083280461Sdim thumbMode = true; 1084280461Sdim break; 1085280461Sdim case modeArmCode: 1086280461Sdim thumbMode = false; 1087280461Sdim break; 1088280461Sdim case modeData: 1089280461Sdim break; 1090280461Sdim case thumb_b22: 1091280461Sdim case thumb_bl22: 1092280461Sdim assert(thumbMode); 1093280461Sdim if (useExternalReloc) 1094280461Sdim displacement = (ref.addend() - (fixupAddress + 4)); 1095280461Sdim else 1096280461Sdim displacement = (targetAddress - (fixupAddress + 4)) + ref.addend(); 1097280461Sdim value32 = setDisplacementInThumbBranch(*loc32, fixupAddress, 1098280461Sdim displacement, 1099280461Sdim targetIsUndef || targetIsThumb); 1100280461Sdim *loc32 = value32; 1101280461Sdim break; 1102280461Sdim case thumb_movw: 1103280461Sdim assert(thumbMode); 1104280461Sdim if (useExternalReloc) 1105280461Sdim value16 = ref.addend() & 0xFFFF; 1106280461Sdim else 1107280461Sdim value16 = (targetAddress + ref.addend()) & 0xFFFF; 1108280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 1109280461Sdim break; 1110280461Sdim case thumb_movt: 1111280461Sdim assert(thumbMode); 1112280461Sdim if (useExternalReloc) 1113280461Sdim value16 = ref.addend() >> 16; 1114280461Sdim else 1115280461Sdim value16 = (targetAddress + ref.addend()) >> 16; 1116280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 1117280461Sdim break; 1118280461Sdim case thumb_movw_funcRel: 1119280461Sdim assert(thumbMode); 1120280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; 1121280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 1122280461Sdim break; 1123280461Sdim case thumb_movt_funcRel: 1124280461Sdim assert(thumbMode); 1125280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; 1126280461Sdim *loc32 = setWordFromThumbMov(*loc32, value16); 1127280461Sdim break; 1128280461Sdim case arm_b24: 1129280461Sdim case arm_bl24: 1130280461Sdim assert(!thumbMode); 1131280461Sdim if (useExternalReloc) 1132280461Sdim displacement = (ref.addend() - (fixupAddress + 8)); 1133280461Sdim else 1134280461Sdim displacement = (targetAddress - (fixupAddress + 8)) + ref.addend(); 1135280461Sdim value32 = setDisplacementInArmBranch(*loc32, displacement, 1136280461Sdim targetIsThumb); 1137280461Sdim *loc32 = value32; 1138280461Sdim break; 1139280461Sdim case arm_movw: 1140280461Sdim assert(!thumbMode); 1141280461Sdim if (useExternalReloc) 1142280461Sdim value16 = ref.addend() & 0xFFFF; 1143280461Sdim else 1144280461Sdim value16 = (targetAddress + ref.addend()) & 0xFFFF; 1145280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 1146280461Sdim break; 1147280461Sdim case arm_movt: 1148280461Sdim assert(!thumbMode); 1149280461Sdim if (useExternalReloc) 1150280461Sdim value16 = ref.addend() >> 16; 1151280461Sdim else 1152280461Sdim value16 = (targetAddress + ref.addend()) >> 16; 1153280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 1154280461Sdim break; 1155280461Sdim case arm_movw_funcRel: 1156280461Sdim assert(!thumbMode); 1157280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) & 0xFFFF; 1158280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 1159280461Sdim break; 1160280461Sdim case arm_movt_funcRel: 1161280461Sdim assert(!thumbMode); 1162280461Sdim value16 = (targetAddress - inAtomAddress + ref.addend()) >> 16; 1163280461Sdim *loc32 = setWordFromArmMov(*loc32, value16); 1164280461Sdim break; 1165280461Sdim case pointer32: 1166280461Sdim *loc32 = targetAddress + ref.addend(); 1167280461Sdim break; 1168280461Sdim case delta32: 1169280461Sdim *loc32 = targetAddress - fixupAddress + ref.addend(); 1170280461Sdim break; 1171280461Sdim case lazyPointer: 1172280461Sdim case lazyImmediateLocation: 1173280461Sdim // do nothing 1174280461Sdim break; 1175280461Sdim case invalid: 1176280461Sdim llvm_unreachable("invalid ARM Reference Kind"); 1177280461Sdim break; 1178280461Sdim } 1179280461Sdim} 1180280461Sdim 1181280461Sdimvoid ArchHandler_arm::appendSectionRelocations( 1182280461Sdim const DefinedAtom &atom, 1183280461Sdim uint64_t atomSectionOffset, 1184280461Sdim const Reference &ref, 1185280461Sdim FindSymbolIndexForAtom symbolIndexForAtom, 1186280461Sdim FindSectionIndexForAtom sectionIndexForAtom, 1187280461Sdim FindAddressForAtom addressForAtom, 1188280461Sdim normalized::Relocations &relocs) { 1189280461Sdim if (ref.kindNamespace() != Reference::KindNamespace::mach_o) 1190280461Sdim return; 1191280461Sdim assert(ref.kindArch() == Reference::KindArch::ARM); 1192280461Sdim uint32_t sectionOffset = atomSectionOffset + ref.offsetInAtom(); 1193280461Sdim bool useExternalReloc = useExternalRelocationTo(*ref.target()); 1194280461Sdim uint32_t targetAtomAddress; 1195280461Sdim uint32_t fromAtomAddress; 1196280461Sdim uint16_t other16; 1197280461Sdim switch (static_cast<ArmKind>(ref.kindValue())) { 1198280461Sdim case modeThumbCode: 1199280461Sdim case modeArmCode: 1200280461Sdim case modeData: 1201280461Sdim // Do nothing. 1202280461Sdim break; 1203280461Sdim case thumb_b22: 1204280461Sdim case thumb_bl22: 1205280461Sdim if (useExternalReloc) { 1206280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1207280461Sdim ARM_THUMB_RELOC_BR22 | rExtern | rPcRel | rLength4); 1208280461Sdim } else { 1209280461Sdim if (ref.addend() != 0) 1210280461Sdim appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), 1211280461Sdim ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4); 1212280461Sdim else 1213280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1214280461Sdim ARM_THUMB_RELOC_BR22 | rPcRel | rLength4); 1215280461Sdim } 1216280461Sdim break; 1217280461Sdim case thumb_movw: 1218280461Sdim if (useExternalReloc) { 1219280461Sdim other16 = ref.addend() >> 16; 1220280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1221280461Sdim ARM_RELOC_HALF | rExtern | rLenThmbLo); 1222280461Sdim appendReloc(relocs, other16, 0, 0, 1223280461Sdim ARM_RELOC_PAIR | rLenThmbLo); 1224280461Sdim } else { 1225280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1226280461Sdim if (ref.addend() != 0) { 1227280461Sdim other16 = (targetAtomAddress + ref.addend()) >> 16; 1228280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1229280461Sdim ARM_RELOC_HALF | rScattered | rLenThmbLo); 1230280461Sdim appendReloc(relocs, other16, 0, 0, 1231280461Sdim ARM_RELOC_PAIR | rLenThmbLo); 1232280461Sdim } else { 1233280461Sdim other16 = (targetAtomAddress + ref.addend()) >> 16; 1234280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1235280461Sdim ARM_RELOC_HALF | rLenThmbLo); 1236280461Sdim appendReloc(relocs, other16, 0, 0, 1237280461Sdim ARM_RELOC_PAIR | rLenThmbLo); 1238280461Sdim } 1239280461Sdim } 1240280461Sdim break; 1241280461Sdim case thumb_movt: 1242280461Sdim if (useExternalReloc) { 1243280461Sdim other16 = ref.addend() & 0xFFFF; 1244280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1245280461Sdim ARM_RELOC_HALF | rExtern | rLenThmbHi); 1246280461Sdim appendReloc(relocs, other16, 0, 0, 1247280461Sdim ARM_RELOC_PAIR | rLenThmbHi); 1248280461Sdim } else { 1249280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1250280461Sdim if (ref.addend() != 0) { 1251280461Sdim other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; 1252280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1253280461Sdim ARM_RELOC_HALF | rScattered | rLenThmbHi); 1254280461Sdim appendReloc(relocs, other16, 0, 0, 1255280461Sdim ARM_RELOC_PAIR | rLenThmbHi); 1256280461Sdim } else { 1257280461Sdim other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; 1258280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1259280461Sdim ARM_RELOC_HALF | rLenThmbHi); 1260280461Sdim appendReloc(relocs, other16, 0, 0, 1261280461Sdim ARM_RELOC_PAIR | rLenThmbHi); 1262280461Sdim } 1263280461Sdim } 1264280461Sdim break; 1265280461Sdim case thumb_movw_funcRel: 1266280461Sdim fromAtomAddress = addressForAtom(atom); 1267280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1268280461Sdim other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; 1269280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1270280461Sdim ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo); 1271280461Sdim appendReloc(relocs, other16, 0, fromAtomAddress, 1272280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbLo); 1273280461Sdim break; 1274280461Sdim case thumb_movt_funcRel: 1275280461Sdim fromAtomAddress = addressForAtom(atom); 1276280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1277280461Sdim other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; 1278280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1279280461Sdim ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi); 1280280461Sdim appendReloc(relocs, other16, 0, fromAtomAddress, 1281280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbHi); 1282280461Sdim break; 1283280461Sdim case arm_b24: 1284280461Sdim case arm_bl24: 1285280461Sdim if (useExternalReloc) { 1286280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1287280461Sdim ARM_RELOC_BR24 | rExtern | rPcRel | rLength4); 1288280461Sdim } else { 1289280461Sdim if (ref.addend() != 0) 1290280461Sdim appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), 1291280461Sdim ARM_RELOC_BR24 | rScattered | rPcRel | rLength4); 1292280461Sdim else 1293280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1294280461Sdim ARM_RELOC_BR24 | rPcRel | rLength4); 1295280461Sdim } 1296280461Sdim break; 1297280461Sdim case arm_movw: 1298280461Sdim if (useExternalReloc) { 1299280461Sdim other16 = ref.addend() >> 16; 1300280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1301280461Sdim ARM_RELOC_HALF | rExtern | rLenArmLo); 1302280461Sdim appendReloc(relocs, other16, 0, 0, 1303280461Sdim ARM_RELOC_PAIR | rLenArmLo); 1304280461Sdim } else { 1305280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1306280461Sdim if (ref.addend() != 0) { 1307280461Sdim other16 = (targetAtomAddress + ref.addend()) >> 16; 1308280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1309280461Sdim ARM_RELOC_HALF | rScattered | rLenArmLo); 1310280461Sdim appendReloc(relocs, other16, 0, 0, 1311280461Sdim ARM_RELOC_PAIR | rLenArmLo); 1312280461Sdim } else { 1313280461Sdim other16 = (targetAtomAddress + ref.addend()) >> 16; 1314280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1315280461Sdim ARM_RELOC_HALF | rLenArmLo); 1316280461Sdim appendReloc(relocs, other16, 0, 0, 1317280461Sdim ARM_RELOC_PAIR | rLenArmLo); 1318280461Sdim } 1319280461Sdim } 1320280461Sdim break; 1321280461Sdim case arm_movt: 1322280461Sdim if (useExternalReloc) { 1323280461Sdim other16 = ref.addend() & 0xFFFF; 1324280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1325280461Sdim ARM_RELOC_HALF | rExtern | rLenArmHi); 1326280461Sdim appendReloc(relocs, other16, 0, 0, 1327280461Sdim ARM_RELOC_PAIR | rLenArmHi); 1328280461Sdim } else { 1329280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1330280461Sdim if (ref.addend() != 0) { 1331280461Sdim other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; 1332280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1333280461Sdim ARM_RELOC_HALF | rScattered | rLenArmHi); 1334280461Sdim appendReloc(relocs, other16, 0, 0, 1335280461Sdim ARM_RELOC_PAIR | rLenArmHi); 1336280461Sdim } else { 1337280461Sdim other16 = (targetAtomAddress + ref.addend()) & 0xFFFF; 1338280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1339280461Sdim ARM_RELOC_HALF | rLenArmHi); 1340280461Sdim appendReloc(relocs, other16, 0, 0, 1341280461Sdim ARM_RELOC_PAIR | rLenArmHi); 1342280461Sdim } 1343280461Sdim } 1344280461Sdim break; 1345280461Sdim case arm_movw_funcRel: 1346280461Sdim fromAtomAddress = addressForAtom(atom); 1347280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1348280461Sdim other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) >> 16; 1349280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1350280461Sdim ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo); 1351280461Sdim appendReloc(relocs, other16, 0, fromAtomAddress, 1352280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmLo); 1353280461Sdim break; 1354280461Sdim case arm_movt_funcRel: 1355280461Sdim fromAtomAddress = addressForAtom(atom); 1356280461Sdim targetAtomAddress = addressForAtom(*ref.target()); 1357280461Sdim other16 = (targetAtomAddress - fromAtomAddress + ref.addend()) & 0xFFFF; 1358280461Sdim appendReloc(relocs, sectionOffset, 0, targetAtomAddress, 1359280461Sdim ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi); 1360280461Sdim appendReloc(relocs, other16, 0, fromAtomAddress, 1361280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmHi); 1362280461Sdim break; 1363280461Sdim case pointer32: 1364280461Sdim if (useExternalReloc) { 1365280461Sdim appendReloc(relocs, sectionOffset, symbolIndexForAtom(*ref.target()), 0, 1366280461Sdim ARM_RELOC_VANILLA | rExtern | rLength4); 1367280461Sdim } 1368280461Sdim else { 1369280461Sdim if (ref.addend() != 0) 1370280461Sdim appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), 1371280461Sdim ARM_RELOC_VANILLA | rScattered | rLength4); 1372280461Sdim else 1373280461Sdim appendReloc(relocs, sectionOffset, sectionIndexForAtom(*ref.target()),0, 1374280461Sdim ARM_RELOC_VANILLA | rLength4); 1375280461Sdim } 1376280461Sdim break; 1377280461Sdim case delta32: 1378280461Sdim appendReloc(relocs, sectionOffset, 0, addressForAtom(*ref.target()), 1379280461Sdim ARM_RELOC_SECTDIFF | rScattered | rLength4); 1380280461Sdim appendReloc(relocs, sectionOffset, 0, addressForAtom(atom) + 1381280461Sdim ref.offsetInAtom(), 1382280461Sdim ARM_RELOC_PAIR | rScattered | rLength4); 1383280461Sdim break; 1384280461Sdim case lazyPointer: 1385280461Sdim case lazyImmediateLocation: 1386280461Sdim // do nothing 1387280461Sdim break; 1388280461Sdim case invalid: 1389280461Sdim llvm_unreachable("invalid ARM Reference Kind"); 1390280461Sdim break; 1391280461Sdim } 1392280461Sdim} 1393280461Sdim 1394280461Sdimvoid ArchHandler_arm::addAdditionalReferences(MachODefinedAtom &atom) { 1395280461Sdim if (atom.isThumb()) { 1396280461Sdim atom.addReference(0, modeThumbCode, &atom, 0, Reference::KindArch::ARM); 1397280461Sdim } 1398280461Sdim} 1399280461Sdim 1400280461Sdimbool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { 1401280461Sdim for (const Reference *ref : atom) { 1402280461Sdim if (ref->offsetInAtom() != 0) 1403280461Sdim return false; 1404280461Sdim if (ref->kindNamespace() != Reference::KindNamespace::mach_o) 1405280461Sdim continue; 1406280461Sdim assert(ref->kindArch() == Reference::KindArch::ARM); 1407280461Sdim if (ref->kindValue() == modeThumbCode) 1408280461Sdim return true; 1409280461Sdim } 1410280461Sdim return false; 1411280461Sdim} 1412280461Sdim 1413280461Sdim 1414280461Sdimclass Thumb2ToArmShimAtom : public SimpleDefinedAtom { 1415280461Sdimpublic: 1416280461Sdim Thumb2ToArmShimAtom(MachOFile &file, StringRef targetName, 1417280461Sdim const DefinedAtom &target) 1418280461Sdim : SimpleDefinedAtom(file) { 1419280461Sdim addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, 1420280461Sdim ArchHandler_arm::modeThumbCode, 0, this, 0); 1421280461Sdim addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, 1422280461Sdim ArchHandler_arm::delta32, 8, &target, 0); 1423280461Sdim std::string name = std::string(targetName) + "$shim"; 1424280461Sdim StringRef tmp(name); 1425280461Sdim _name = tmp.copy(file.allocator()); 1426280461Sdim } 1427280461Sdim 1428280461Sdim StringRef name() const override { 1429280461Sdim return _name; 1430280461Sdim } 1431280461Sdim 1432280461Sdim ContentType contentType() const override { 1433280461Sdim return DefinedAtom::typeCode; 1434280461Sdim } 1435280461Sdim 1436280461Sdim Alignment alignment() const override { 1437280461Sdim return Alignment(2); 1438280461Sdim } 1439280461Sdim 1440280461Sdim uint64_t size() const override { 1441280461Sdim return 12; 1442280461Sdim } 1443280461Sdim 1444280461Sdim ContentPermissions permissions() const override { 1445280461Sdim return DefinedAtom::permR_X; 1446280461Sdim } 1447280461Sdim 1448280461Sdim ArrayRef<uint8_t> rawContent() const override { 1449280461Sdim static const uint8_t bytes[] = 1450280461Sdim { 0xDF, 0xF8, 0x04, 0xC0, // ldr ip, pc + 4 1451280461Sdim 0xFF, 0x44, // add ip, pc, ip 1452280461Sdim 0x60, 0x47, // ldr pc, [ip] 1453280461Sdim 0x00, 0x00, 0x00, 0x00 }; // .long target - this 1454280461Sdim assert(sizeof(bytes) == size()); 1455280461Sdim return llvm::makeArrayRef(bytes, sizeof(bytes)); 1456280461Sdim } 1457280461Sdimprivate: 1458280461Sdim StringRef _name; 1459280461Sdim}; 1460280461Sdim 1461280461Sdim 1462280461Sdimclass ArmToThumbShimAtom : public SimpleDefinedAtom { 1463280461Sdimpublic: 1464280461Sdim ArmToThumbShimAtom(MachOFile &file, StringRef targetName, 1465280461Sdim const DefinedAtom &target) 1466280461Sdim : SimpleDefinedAtom(file) { 1467280461Sdim addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, 1468280461Sdim ArchHandler_arm::delta32, 12, &target, 0); 1469280461Sdim std::string name = std::string(targetName) + "$shim"; 1470280461Sdim StringRef tmp(name); 1471280461Sdim _name = tmp.copy(file.allocator()); 1472280461Sdim } 1473280461Sdim 1474280461Sdim StringRef name() const override { 1475280461Sdim return _name; 1476280461Sdim } 1477280461Sdim 1478280461Sdim ContentType contentType() const override { 1479280461Sdim return DefinedAtom::typeCode; 1480280461Sdim } 1481280461Sdim 1482280461Sdim Alignment alignment() const override { 1483280461Sdim return Alignment(2); 1484280461Sdim } 1485280461Sdim 1486280461Sdim uint64_t size() const override { 1487280461Sdim return 16; 1488280461Sdim } 1489280461Sdim 1490280461Sdim ContentPermissions permissions() const override { 1491280461Sdim return DefinedAtom::permR_X; 1492280461Sdim } 1493280461Sdim 1494280461Sdim ArrayRef<uint8_t> rawContent() const override { 1495280461Sdim static const uint8_t bytes[] = 1496280461Sdim { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 1497280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 1498280461Sdim 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] 1499280461Sdim 0x00, 0x00, 0x00, 0x00 }; // .long target - this 1500280461Sdim assert(sizeof(bytes) == size()); 1501280461Sdim return llvm::makeArrayRef(bytes, sizeof(bytes)); 1502280461Sdim } 1503280461Sdimprivate: 1504280461Sdim StringRef _name; 1505280461Sdim}; 1506280461Sdim 1507280461Sdimconst DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, 1508280461Sdim bool thumbToArm, 1509280461Sdim const DefinedAtom &target) { 1510280461Sdim bool isStub = (target.contentType() == DefinedAtom::typeStub); 1511280461Sdim StringRef targetName = isStub ? stubName(target) : target.name(); 1512280461Sdim if (thumbToArm) 1513280461Sdim return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); 1514280461Sdim else 1515280461Sdim return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); 1516280461Sdim} 1517280461Sdim 1518280461Sdim 1519280461Sdimstd::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() { 1520280461Sdim return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm()); 1521280461Sdim} 1522280461Sdim 1523280461Sdim} // namespace mach_o 1524280461Sdim} // namespace lld 1525