ArchHandler_arm.cpp revision 353358
1280461Sdim//===- lib/FileFormat/MachO/ArchHandler_arm.cpp ---------------------------===// 2280461Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6280461Sdim// 7280461Sdim//===----------------------------------------------------------------------===// 8280461Sdim 9280461Sdim#include "ArchHandler.h" 10280461Sdim#include "Atoms.h" 11280461Sdim#include "MachONormalizedFileBinaryUtils.h" 12280461Sdim#include "llvm/ADT/StringRef.h" 13280461Sdim#include "llvm/ADT/StringSwitch.h" 14280461Sdim#include "llvm/ADT/Triple.h" 15280461Sdim#include "llvm/Support/Endian.h" 16280461Sdim#include "llvm/Support/ErrorHandling.h" 17280461Sdim 18280461Sdimusing namespace llvm::MachO; 19280461Sdimusing namespace lld::mach_o::normalized; 20280461Sdim 21280461Sdimnamespace lld { 22280461Sdimnamespace mach_o { 23280461Sdim 24280461Sdimusing llvm::support::ulittle32_t; 25280461Sdimusing llvm::support::little32_t; 26280461Sdim 27280461Sdim 28280461Sdimclass ArchHandler_arm : public ArchHandler { 29280461Sdimpublic: 30292934Sdim ArchHandler_arm() = default; 31292934Sdim ~ArchHandler_arm() override = default; 32280461Sdim 33280461Sdim const Registry::KindStrings *kindStrings() override { return _sKindStrings; } 34280461Sdim 35280461Sdim Reference::KindArch kindArch() override { return Reference::KindArch::ARM; } 36280461Sdim 37280461Sdim const ArchHandler::StubInfo &stubInfo() override; 38280461Sdim bool isCallSite(const Reference &) override; 39280461Sdim bool isPointer(const Reference &) override; 40280461Sdim bool isPairedReloc(const normalized::Relocation &) override; 41280461Sdim bool isNonCallBranch(const Reference &) override; 42280461Sdim 43280461Sdim bool needsCompactUnwind() override { 44280461Sdim return false; 45280461Sdim } 46280461Sdim Reference::KindValue imageOffsetKind() override { 47280461Sdim return invalid; 48280461Sdim } 49280461Sdim Reference::KindValue imageOffsetKindIndirect() override { 50280461Sdim return invalid; 51280461Sdim } 52280461Sdim 53303239Sdim Reference::KindValue unwindRefToPersonalityFunctionKind() override { 54303239Sdim return invalid; 55303239Sdim } 56303239Sdim 57280461Sdim Reference::KindValue unwindRefToCIEKind() override { 58280461Sdim return invalid; 59280461Sdim } 60280461Sdim 61280461Sdim Reference::KindValue unwindRefToFunctionKind() override { 62280461Sdim return invalid; 63280461Sdim } 64280461Sdim 65280461Sdim Reference::KindValue unwindRefToEhFrameKind() override { 66280461Sdim return invalid; 67280461Sdim } 68280461Sdim 69326909Sdim Reference::KindValue lazyImmediateLocationKind() override { 70326909Sdim return lazyImmediateLocation; 71326909Sdim } 72326909Sdim 73303239Sdim Reference::KindValue pointerKind() override { 74303239Sdim return invalid; 75303239Sdim } 76303239Sdim 77280461Sdim uint32_t dwarfCompactUnwindType() override { 78280461Sdim // FIXME 79280461Sdim return -1; 80280461Sdim } 81280461Sdim 82303239Sdim llvm::Error getReferenceInfo(const normalized::Relocation &reloc, 83303239Sdim const DefinedAtom *inAtom, 84303239Sdim uint32_t offsetInAtom, 85303239Sdim uint64_t fixupAddress, bool swap, 86303239Sdim FindAtomBySectionAndAddress atomFromAddress, 87303239Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 88303239Sdim Reference::KindValue *kind, 89303239Sdim const lld::Atom **target, 90303239Sdim Reference::Addend *addend) override; 91303239Sdim llvm::Error 92280461Sdim getPairReferenceInfo(const normalized::Relocation &reloc1, 93280461Sdim const normalized::Relocation &reloc2, 94280461Sdim const DefinedAtom *inAtom, 95280461Sdim uint32_t offsetInAtom, 96280461Sdim uint64_t fixupAddress, bool swap, bool scatterable, 97280461Sdim FindAtomBySectionAndAddress atomFromAddress, 98280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 99280461Sdim Reference::KindValue *kind, 100280461Sdim const lld::Atom **target, 101280461Sdim Reference::Addend *addend) override; 102280461Sdim 103280461Sdim void generateAtomContent(const DefinedAtom &atom, bool relocatable, 104280461Sdim FindAddressForAtom findAddress, 105280461Sdim FindAddressForAtom findSectionAddress, 106280461Sdim uint64_t imageBaseAddress, 107303239Sdim llvm::MutableArrayRef<uint8_t> atomContentBuffer) override; 108280461Sdim 109280461Sdim void appendSectionRelocations(const DefinedAtom &atom, 110280461Sdim uint64_t atomSectionOffset, 111280461Sdim const Reference &ref, 112280461Sdim FindSymbolIndexForAtom, 113280461Sdim FindSectionIndexForAtom, 114280461Sdim FindAddressForAtom, 115280461Sdim normalized::Relocations &) override; 116280461Sdim 117280461Sdim void addAdditionalReferences(MachODefinedAtom &atom) override; 118280461Sdim 119280461Sdim bool isDataInCodeTransition(Reference::KindValue refKind) override { 120280461Sdim switch (refKind) { 121280461Sdim case modeThumbCode: 122280461Sdim case modeArmCode: 123280461Sdim case modeData: 124280461Sdim return true; 125280461Sdim default: 126280461Sdim return false; 127280461Sdim break; 128280461Sdim } 129280461Sdim } 130280461Sdim 131280461Sdim Reference::KindValue dataInCodeTransitionStart( 132280461Sdim const MachODefinedAtom &atom) override { 133280461Sdim return modeData; 134280461Sdim } 135280461Sdim 136280461Sdim Reference::KindValue dataInCodeTransitionEnd( 137280461Sdim const MachODefinedAtom &atom) override { 138280461Sdim return atom.isThumb() ? modeThumbCode : modeArmCode; 139280461Sdim } 140280461Sdim 141280461Sdim bool isThumbFunction(const DefinedAtom &atom) override; 142280461Sdim const DefinedAtom *createShim(MachOFile &file, bool thumbToArm, 143280461Sdim const DefinedAtom &) override; 144280461Sdim 145280461Sdimprivate: 146280461Sdim friend class Thumb2ToArmShimAtom; 147280461Sdim friend class ArmToThumbShimAtom; 148280461Sdim 149280461Sdim static const Registry::KindStrings _sKindStrings[]; 150280461Sdim static const StubInfo _sStubInfoArmPIC; 151280461Sdim 152280461Sdim enum ArmKind : Reference::KindValue { 153280461Sdim invalid, /// for error condition 154280461Sdim 155280461Sdim modeThumbCode, /// Content starting at this offset is thumb. 156280461Sdim modeArmCode, /// Content starting at this offset is arm. 157280461Sdim modeData, /// Content starting at this offset is data. 158280461Sdim 159280461Sdim // Kinds found in mach-o .o files: 160280461Sdim thumb_bl22, /// ex: bl _foo 161280461Sdim thumb_b22, /// ex: b _foo 162280461Sdim thumb_movw, /// ex: movw r1, :lower16:_foo 163280461Sdim thumb_movt, /// ex: movt r1, :lower16:_foo 164280461Sdim thumb_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) 165280461Sdim thumb_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) 166280461Sdim arm_bl24, /// ex: bl _foo 167280461Sdim arm_b24, /// ex: b _foo 168280461Sdim arm_movw, /// ex: movw r1, :lower16:_foo 169280461Sdim arm_movt, /// ex: movt r1, :lower16:_foo 170280461Sdim arm_movw_funcRel, /// ex: movw r1, :lower16:(_foo-(L1+4)) 171280461Sdim arm_movt_funcRel, /// ex: movt r1, :upper16:(_foo-(L1+4)) 172280461Sdim pointer32, /// ex: .long _foo 173280461Sdim delta32, /// ex: .long _foo - . 174280461Sdim 175280461Sdim // Kinds introduced by Passes: 176280461Sdim lazyPointer, /// Location contains a lazy pointer. 177280461Sdim lazyImmediateLocation, /// Location contains immediate value used in stub. 178280461Sdim }; 179280461Sdim 180280461Sdim // Utility functions for inspecting/updating instructions. 181280461Sdim static bool isThumbMovw(uint32_t instruction); 182280461Sdim static bool isThumbMovt(uint32_t instruction); 183280461Sdim static bool isArmMovw(uint32_t instruction); 184280461Sdim static bool isArmMovt(uint32_t instruction); 185280461Sdim static int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t); 186280461Sdim static int32_t getDisplacementFromArmBranch(uint32_t instruction); 187280461Sdim static uint16_t getWordFromThumbMov(uint32_t instruction); 188280461Sdim static uint16_t getWordFromArmMov(uint32_t instruction); 189280461Sdim static uint32_t clearThumbBit(uint32_t value, const Atom *target); 190280461Sdim static uint32_t setDisplacementInArmBranch(uint32_t instr, int32_t disp, 191280461Sdim bool targetIsThumb); 192280461Sdim static uint32_t setDisplacementInThumbBranch(uint32_t instr, uint32_t ia, 193280461Sdim int32_t disp, bool targetThumb); 194280461Sdim static uint32_t setWordFromThumbMov(uint32_t instruction, uint16_t word); 195280461Sdim static uint32_t setWordFromArmMov(uint32_t instruction, uint16_t word); 196280461Sdim 197280461Sdim StringRef stubName(const DefinedAtom &); 198280461Sdim bool useExternalRelocationTo(const Atom &target); 199280461Sdim 200280461Sdim void applyFixupFinal(const Reference &ref, uint8_t *location, 201280461Sdim uint64_t fixupAddress, uint64_t targetAddress, 202280461Sdim uint64_t inAtomAddress, bool &thumbMode, 203280461Sdim bool targetIsThumb); 204280461Sdim 205280461Sdim void applyFixupRelocatable(const Reference &ref, uint8_t *location, 206280461Sdim uint64_t fixupAddress, 207280461Sdim uint64_t targetAddress, 208280461Sdim uint64_t inAtomAddress, bool &thumbMode, 209280461Sdim bool targetIsThumb); 210280461Sdim}; 211280461Sdim 212280461Sdim//===----------------------------------------------------------------------===// 213280461Sdim// ArchHandler_arm 214280461Sdim//===----------------------------------------------------------------------===// 215280461Sdim 216280461Sdimconst Registry::KindStrings ArchHandler_arm::_sKindStrings[] = { 217280461Sdim LLD_KIND_STRING_ENTRY(invalid), 218280461Sdim LLD_KIND_STRING_ENTRY(modeThumbCode), 219280461Sdim LLD_KIND_STRING_ENTRY(modeArmCode), 220280461Sdim LLD_KIND_STRING_ENTRY(modeData), 221280461Sdim LLD_KIND_STRING_ENTRY(thumb_bl22), 222280461Sdim LLD_KIND_STRING_ENTRY(thumb_b22), 223280461Sdim LLD_KIND_STRING_ENTRY(thumb_movw), 224280461Sdim LLD_KIND_STRING_ENTRY(thumb_movt), 225280461Sdim LLD_KIND_STRING_ENTRY(thumb_movw_funcRel), 226280461Sdim LLD_KIND_STRING_ENTRY(thumb_movt_funcRel), 227280461Sdim LLD_KIND_STRING_ENTRY(arm_bl24), 228280461Sdim LLD_KIND_STRING_ENTRY(arm_b24), 229280461Sdim LLD_KIND_STRING_ENTRY(arm_movw), 230280461Sdim LLD_KIND_STRING_ENTRY(arm_movt), 231280461Sdim LLD_KIND_STRING_ENTRY(arm_movw_funcRel), 232280461Sdim LLD_KIND_STRING_ENTRY(arm_movt_funcRel), 233280461Sdim LLD_KIND_STRING_ENTRY(pointer32), 234280461Sdim LLD_KIND_STRING_ENTRY(delta32), 235280461Sdim LLD_KIND_STRING_ENTRY(lazyPointer), 236280461Sdim LLD_KIND_STRING_ENTRY(lazyImmediateLocation), 237280461Sdim LLD_KIND_STRING_END 238280461Sdim}; 239280461Sdim 240280461Sdimconst ArchHandler::StubInfo ArchHandler_arm::_sStubInfoArmPIC = { 241280461Sdim "dyld_stub_binder", 242280461Sdim 243280461Sdim // References in lazy pointer 244280461Sdim { Reference::KindArch::ARM, pointer32, 0, 0 }, 245280461Sdim { Reference::KindArch::ARM, lazyPointer, 0, 0 }, 246280461Sdim 247280461Sdim // GOT pointer to dyld_stub_binder 248280461Sdim { Reference::KindArch::ARM, pointer32, 0, 0 }, 249280461Sdim 250280461Sdim // arm code alignment 2^2 251280461Sdim 2, 252280461Sdim 253280461Sdim // Stub size and code 254280461Sdim 16, 255280461Sdim { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 12 256280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 257280461Sdim 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 258280461Sdim 0x00, 0x00, 0x00, 0x00 }, // .long L_foo$lazy_ptr - (L1$scv + 8) 259280461Sdim { Reference::KindArch::ARM, delta32, 12, 0 }, 260280461Sdim { false, 0, 0, 0 }, 261280461Sdim 262280461Sdim // Stub Helper size and code 263280461Sdim 12, 264280461Sdim { 0x00, 0xC0, 0x9F, 0xE5, // ldr ip, [pc, #0] 265280461Sdim 0x00, 0x00, 0x00, 0xEA, // b _helperhelper 266280461Sdim 0x00, 0x00, 0x00, 0x00 }, // .long lazy-info-offset 267280461Sdim { Reference::KindArch::ARM, lazyImmediateLocation, 8, 0 }, 268280461Sdim { Reference::KindArch::ARM, arm_b24, 4, 0 }, 269280461Sdim 270303239Sdim // Stub helper image cache content type 271303239Sdim DefinedAtom::typeGOT, 272303239Sdim 273280461Sdim // Stub Helper-Common size and code 274280461Sdim 36, 275303239Sdim // Stub helper alignment 276303239Sdim 2, 277280461Sdim { // push lazy-info-offset 278280461Sdim 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! 279280461Sdim // push address of dyld_mageLoaderCache 280280461Sdim 0x10, 0xC0, 0x9F, 0xE5, // ldr ip, L1 281280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 282280461Sdim 0x04, 0xC0, 0x2D, 0xE5, // str ip, [sp, #-4]! 283280461Sdim // jump through dyld_stub_binder 284280461Sdim 0x08, 0xC0, 0x9F, 0xE5, // ldr ip, L2 285280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 286280461Sdim 0x00, 0xF0, 0x9C, 0xE5, // ldr pc, [ip] 287280461Sdim 0x00, 0x00, 0x00, 0x00, // L1: .long fFastStubGOTAtom - (helper+16) 288280461Sdim 0x00, 0x00, 0x00, 0x00 }, // L2: .long dyld_stub_binder - (helper+28) 289280461Sdim { Reference::KindArch::ARM, delta32, 28, 0xC }, 290280461Sdim { false, 0, 0, 0 }, 291280461Sdim { Reference::KindArch::ARM, delta32, 32, 0x04 }, 292280461Sdim { false, 0, 0, 0 } 293280461Sdim}; 294280461Sdim 295280461Sdimconst ArchHandler::StubInfo &ArchHandler_arm::stubInfo() { 296280461Sdim // If multiple kinds of stubs are supported, select which StubInfo here. 297280461Sdim return _sStubInfoArmPIC; 298280461Sdim} 299280461Sdim 300280461Sdimbool ArchHandler_arm::isCallSite(const Reference &ref) { 301280461Sdim switch (ref.kindValue()) { 302280461Sdim case thumb_b22: 303280461Sdim case thumb_bl22: 304280461Sdim case arm_b24: 305280461Sdim case arm_bl24: 306280461Sdim return true; 307280461Sdim default: 308280461Sdim return false; 309280461Sdim } 310280461Sdim} 311280461Sdim 312280461Sdimbool ArchHandler_arm::isPointer(const Reference &ref) { 313280461Sdim return (ref.kindValue() == pointer32); 314280461Sdim} 315280461Sdim 316280461Sdimbool ArchHandler_arm::isNonCallBranch(const Reference &ref) { 317280461Sdim switch (ref.kindValue()) { 318280461Sdim case thumb_b22: 319280461Sdim case arm_b24: 320280461Sdim return true; 321280461Sdim default: 322280461Sdim return false; 323280461Sdim } 324280461Sdim} 325280461Sdim 326280461Sdimbool ArchHandler_arm::isPairedReloc(const Relocation &reloc) { 327280461Sdim switch (reloc.type) { 328280461Sdim case ARM_RELOC_SECTDIFF: 329280461Sdim case ARM_RELOC_LOCAL_SECTDIFF: 330280461Sdim case ARM_RELOC_HALF_SECTDIFF: 331280461Sdim case ARM_RELOC_HALF: 332280461Sdim return true; 333280461Sdim default: 334280461Sdim return false; 335280461Sdim } 336280461Sdim} 337280461Sdim 338280461Sdim/// Trace references from stub atom to lazy pointer to target and get its name. 339280461SdimStringRef ArchHandler_arm::stubName(const DefinedAtom &stubAtom) { 340280461Sdim assert(stubAtom.contentType() == DefinedAtom::typeStub); 341280461Sdim for (const Reference *ref : stubAtom) { 342280461Sdim if (const DefinedAtom* lp = dyn_cast<DefinedAtom>(ref->target())) { 343280461Sdim if (lp->contentType() != DefinedAtom::typeLazyPointer) 344280461Sdim continue; 345280461Sdim for (const Reference *ref2 : *lp) { 346280461Sdim if (ref2->kindValue() != lazyPointer) 347280461Sdim continue; 348280461Sdim return ref2->target()->name(); 349280461Sdim } 350280461Sdim } 351280461Sdim } 352280461Sdim return "stub"; 353280461Sdim} 354280461Sdim 355280461Sdim/// Extract displacement from an ARM b/bl/blx instruction. 356280461Sdimint32_t ArchHandler_arm::getDisplacementFromArmBranch(uint32_t instruction) { 357280461Sdim // Sign-extend imm24 358280461Sdim int32_t displacement = (instruction & 0x00FFFFFF) << 2; 359280461Sdim if ((displacement & 0x02000000) != 0) 360280461Sdim displacement |= 0xFC000000; 361280461Sdim // If this is BLX and H bit set, add 2. 362280461Sdim if ((instruction & 0xFF000000) == 0xFB000000) 363280461Sdim displacement += 2; 364280461Sdim return displacement; 365280461Sdim} 366280461Sdim 367280461Sdim/// Update an ARM b/bl/blx instruction, switching bl <-> blx as needed. 368280461Sdimuint32_t ArchHandler_arm::setDisplacementInArmBranch(uint32_t instruction, 369280461Sdim int32_t displacement, 370280461Sdim bool targetIsThumb) { 371280461Sdim assert((displacement <= 33554428) && (displacement > (-33554432)) 372280461Sdim && "arm branch out of range"); 373280461Sdim bool is_blx = ((instruction & 0xF0000000) == 0xF0000000); 374280461Sdim uint32_t newInstruction = (instruction & 0xFF000000); 375280461Sdim uint32_t h = 0; 376280461Sdim if (targetIsThumb) { 377280461Sdim // Force use of BLX. 378280461Sdim newInstruction = 0xFA000000; 379280461Sdim if (!is_blx) { 380280461Sdim assert(((instruction & 0xF0000000) == 0xE0000000) 381280461Sdim && "no conditional arm blx"); 382280461Sdim assert(((instruction & 0xFF000000) == 0xEB000000) 383280461Sdim && "no arm pc-rel BX instruction"); 384280461Sdim } 385280461Sdim if (displacement & 2) 386280461Sdim h = 1; 387280461Sdim } 388280461Sdim else { 389280461Sdim // Force use of B/BL. 390280461Sdim if (is_blx) 391280461Sdim newInstruction = 0xEB000000; 392280461Sdim } 393280461Sdim newInstruction |= (h << 24) | ((displacement >> 2) & 0x00FFFFFF); 394280461Sdim return newInstruction; 395280461Sdim} 396280461Sdim 397280461Sdim/// Extract displacement from a thumb b/bl/blx instruction. 398280461Sdimint32_t ArchHandler_arm::getDisplacementFromThumbBranch(uint32_t instruction, 399280461Sdim uint32_t instrAddr) { 400280461Sdim bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); 401280461Sdim uint32_t s = (instruction >> 10) & 0x1; 402280461Sdim uint32_t j1 = (instruction >> 29) & 0x1; 403280461Sdim uint32_t j2 = (instruction >> 27) & 0x1; 404280461Sdim uint32_t imm10 = instruction & 0x3FF; 405280461Sdim uint32_t imm11 = (instruction >> 16) & 0x7FF; 406280461Sdim uint32_t i1 = (j1 == s); 407280461Sdim uint32_t i2 = (j2 == s); 408280461Sdim uint32_t dis = 409280461Sdim (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); 410280461Sdim int32_t sdis = dis; 411280461Sdim int32_t result = s ? (sdis | 0xFE000000) : sdis; 412280461Sdim if (is_blx && (instrAddr & 0x2)) { 413280461Sdim // The thumb blx instruction always has low bit of imm11 as zero. The way 414280461Sdim // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that 415280461Sdim // the blx instruction always 4-byte aligns the pc before adding the 416280461Sdim // displacement from the blx. We must emulate that when decoding this. 417280461Sdim result -= 2; 418280461Sdim } 419280461Sdim return result; 420280461Sdim} 421280461Sdim 422280461Sdim/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. 423280461Sdimuint32_t ArchHandler_arm::setDisplacementInThumbBranch(uint32_t instruction, 424280461Sdim uint32_t instrAddr, 425280461Sdim int32_t displacement, 426280461Sdim bool targetIsThumb) { 427280461Sdim assert((displacement <= 16777214) && (displacement > (-16777216)) 428280461Sdim && "thumb branch out of range"); 429280461Sdim bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); 430280461Sdim bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); 431280461Sdim bool is_b = ((instruction & 0xD000F800) == 0x9000F000); 432280461Sdim uint32_t newInstruction = (instruction & 0xD000F800); 433280461Sdim if (is_bl || is_blx) { 434280461Sdim if (targetIsThumb) { 435280461Sdim newInstruction = 0xD000F000; // Use bl 436280461Sdim } else { 437280461Sdim newInstruction = 0xC000F000; // Use blx 438280461Sdim // See note in getDisplacementFromThumbBranch() about blx. 439280461Sdim if (instrAddr & 0x2) 440280461Sdim displacement += 2; 441280461Sdim } 442280461Sdim } else if (is_b) { 443280461Sdim assert(targetIsThumb && "no pc-rel thumb branch instruction that " 444280461Sdim "switches to arm mode"); 445280461Sdim } 446280461Sdim else { 447280461Sdim llvm_unreachable("thumb branch22 reloc on a non-branch instruction"); 448280461Sdim } 449280461Sdim uint32_t s = (uint32_t)(displacement >> 24) & 0x1; 450280461Sdim uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; 451280461Sdim uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; 452280461Sdim uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; 453280461Sdim uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; 454280461Sdim uint32_t j1 = (i1 == s); 455280461Sdim uint32_t j2 = (i2 == s); 456280461Sdim uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; 457280461Sdim uint32_t firstDisp = (s << 10) | imm10; 458280461Sdim newInstruction |= (nextDisp << 16) | firstDisp; 459280461Sdim return newInstruction; 460280461Sdim} 461280461Sdim 462280461Sdimbool ArchHandler_arm::isThumbMovw(uint32_t instruction) { 463280461Sdim return (instruction & 0x8000FBF0) == 0x0000F240; 464280461Sdim} 465280461Sdim 466280461Sdimbool ArchHandler_arm::isThumbMovt(uint32_t instruction) { 467280461Sdim return (instruction & 0x8000FBF0) == 0x0000F2C0; 468280461Sdim} 469280461Sdim 470280461Sdimbool ArchHandler_arm::isArmMovw(uint32_t instruction) { 471280461Sdim return (instruction & 0x0FF00000) == 0x03000000; 472280461Sdim} 473280461Sdim 474280461Sdimbool ArchHandler_arm::isArmMovt(uint32_t instruction) { 475280461Sdim return (instruction & 0x0FF00000) == 0x03400000; 476280461Sdim} 477280461Sdim 478280461Sdimuint16_t ArchHandler_arm::getWordFromThumbMov(uint32_t instruction) { 479280461Sdim assert(isThumbMovw(instruction) || isThumbMovt(instruction)); 480280461Sdim uint32_t i = ((instruction & 0x00000400) >> 10); 481280461Sdim uint32_t imm4 = (instruction & 0x0000000F); 482280461Sdim uint32_t imm3 = ((instruction & 0x70000000) >> 28); 483280461Sdim uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); 484280461Sdim return (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; 485280461Sdim} 486280461Sdim 487280461Sdimuint16_t ArchHandler_arm::getWordFromArmMov(uint32_t instruction) { 488280461Sdim assert(isArmMovw(instruction) || isArmMovt(instruction)); 489280461Sdim uint32_t imm4 = ((instruction & 0x000F0000) >> 16); 490280461Sdim uint32_t imm12 = (instruction & 0x00000FFF); 491280461Sdim return (imm4 << 12) | imm12; 492280461Sdim} 493280461Sdim 494280461Sdimuint32_t ArchHandler_arm::setWordFromThumbMov(uint32_t instr, uint16_t word) { 495280461Sdim assert(isThumbMovw(instr) || isThumbMovt(instr)); 496280461Sdim uint32_t imm4 = (word & 0xF000) >> 12; 497280461Sdim uint32_t i = (word & 0x0800) >> 11; 498280461Sdim uint32_t imm3 = (word & 0x0700) >> 8; 499280461Sdim uint32_t imm8 = word & 0x00FF; 500280461Sdim return (instr & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); 501280461Sdim} 502280461Sdim 503280461Sdimuint32_t ArchHandler_arm::setWordFromArmMov(uint32_t instr, uint16_t word) { 504280461Sdim assert(isArmMovw(instr) || isArmMovt(instr)); 505280461Sdim uint32_t imm4 = (word & 0xF000) >> 12; 506280461Sdim uint32_t imm12 = word & 0x0FFF; 507280461Sdim return (instr & 0xFFF0F000) | (imm4 << 16) | imm12; 508280461Sdim} 509280461Sdim 510280461Sdimuint32_t ArchHandler_arm::clearThumbBit(uint32_t value, const Atom *target) { 511280461Sdim // The assembler often adds one to the address of a thumb function. 512280461Sdim // We need to undo that so it does not look like an addend. 513280461Sdim if (value & 1) { 514280461Sdim if (isa<DefinedAtom>(target)) { 515280461Sdim const MachODefinedAtom *machoTarget = 516280461Sdim reinterpret_cast<const MachODefinedAtom *>(target); 517280461Sdim if (machoTarget->isThumb()) 518280461Sdim value &= -2; // mask off thumb-bit 519280461Sdim } 520280461Sdim } 521280461Sdim return value; 522280461Sdim} 523280461Sdim 524303239Sdimllvm::Error ArchHandler_arm::getReferenceInfo( 525280461Sdim const Relocation &reloc, const DefinedAtom *inAtom, uint32_t offsetInAtom, 526280461Sdim uint64_t fixupAddress, bool isBig, 527280461Sdim FindAtomBySectionAndAddress atomFromAddress, 528280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, Reference::KindValue *kind, 529280461Sdim const lld::Atom **target, Reference::Addend *addend) { 530280461Sdim const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; 531280461Sdim uint64_t targetAddress; 532280461Sdim uint32_t instruction = *(const ulittle32_t *)fixupContent; 533280461Sdim int32_t displacement; 534280461Sdim switch (relocPattern(reloc)) { 535280461Sdim case ARM_THUMB_RELOC_BR22 | rPcRel | rExtern | rLength4: 536280461Sdim // ex: bl _foo (and _foo is undefined) 537280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 538280461Sdim *kind = thumb_b22; 539280461Sdim else 540280461Sdim *kind = thumb_bl22; 541303239Sdim if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) 542280461Sdim return ec; 543280461Sdim // Instruction contains branch to addend. 544280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 545280461Sdim *addend = fixupAddress + 4 + displacement; 546314564Sdim return llvm::Error::success(); 547280461Sdim case ARM_THUMB_RELOC_BR22 | rPcRel | rLength4: 548280461Sdim // ex: bl _foo (and _foo is defined) 549280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 550280461Sdim *kind = thumb_b22; 551280461Sdim else 552280461Sdim *kind = thumb_bl22; 553280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 554280461Sdim targetAddress = fixupAddress + 4 + displacement; 555280461Sdim return atomFromAddress(reloc.symbol, targetAddress, target, addend); 556280461Sdim case ARM_THUMB_RELOC_BR22 | rScattered | rPcRel | rLength4: 557280461Sdim // ex: bl _foo+4 (and _foo is defined) 558280461Sdim if ((instruction & 0xD000F800) == 0x9000F000) 559280461Sdim *kind = thumb_b22; 560280461Sdim else 561280461Sdim *kind = thumb_bl22; 562280461Sdim displacement = getDisplacementFromThumbBranch(instruction, fixupAddress); 563280461Sdim targetAddress = fixupAddress + 4 + displacement; 564303239Sdim if (auto ec = atomFromAddress(0, reloc.value, target, addend)) 565280461Sdim return ec; 566280461Sdim // reloc.value is target atom's address. Instruction contains branch 567280461Sdim // to atom+addend. 568280461Sdim *addend += (targetAddress - reloc.value); 569314564Sdim return llvm::Error::success(); 570280461Sdim case ARM_RELOC_BR24 | rPcRel | rExtern | rLength4: 571280461Sdim // ex: bl _foo (and _foo is undefined) 572280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 573280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 574280461Sdim *kind = arm_b24; 575280461Sdim else 576280461Sdim *kind = arm_bl24; 577303239Sdim if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) 578280461Sdim return ec; 579280461Sdim // Instruction contains branch to addend. 580280461Sdim displacement = getDisplacementFromArmBranch(instruction); 581280461Sdim *addend = fixupAddress + 8 + displacement; 582314564Sdim return llvm::Error::success(); 583280461Sdim case ARM_RELOC_BR24 | rPcRel | rLength4: 584280461Sdim // ex: bl _foo (and _foo is defined) 585280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 586280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 587280461Sdim *kind = arm_b24; 588280461Sdim else 589280461Sdim *kind = arm_bl24; 590280461Sdim displacement = getDisplacementFromArmBranch(instruction); 591280461Sdim targetAddress = fixupAddress + 8 + displacement; 592280461Sdim return atomFromAddress(reloc.symbol, targetAddress, target, addend); 593280461Sdim case ARM_RELOC_BR24 | rScattered | rPcRel | rLength4: 594280461Sdim // ex: bl _foo+4 (and _foo is defined) 595280461Sdim if (((instruction & 0x0F000000) == 0x0A000000) 596280461Sdim && ((instruction & 0xF0000000) != 0xF0000000)) 597280461Sdim *kind = arm_b24; 598280461Sdim else 599280461Sdim *kind = arm_bl24; 600280461Sdim displacement = getDisplacementFromArmBranch(instruction); 601280461Sdim targetAddress = fixupAddress + 8 + displacement; 602303239Sdim if (auto ec = atomFromAddress(0, reloc.value, target, addend)) 603280461Sdim return ec; 604280461Sdim // reloc.value is target atom's address. Instruction contains branch 605280461Sdim // to atom+addend. 606280461Sdim *addend += (targetAddress - reloc.value); 607314564Sdim return llvm::Error::success(); 608280461Sdim case ARM_RELOC_VANILLA | rExtern | rLength4: 609280461Sdim // ex: .long _foo (and _foo is undefined) 610280461Sdim *kind = pointer32; 611303239Sdim if (auto ec = atomFromSymbolIndex(reloc.symbol, target)) 612280461Sdim return ec; 613280461Sdim *addend = instruction; 614314564Sdim return llvm::Error::success(); 615280461Sdim case ARM_RELOC_VANILLA | rLength4: 616280461Sdim // ex: .long _foo (and _foo is defined) 617280461Sdim *kind = pointer32; 618303239Sdim if (auto ec = atomFromAddress(reloc.symbol, instruction, target, addend)) 619280461Sdim return ec; 620280461Sdim *addend = clearThumbBit((uint32_t) * addend, *target); 621314564Sdim return llvm::Error::success(); 622280461Sdim case ARM_RELOC_VANILLA | rScattered | rLength4: 623280461Sdim // ex: .long _foo+a (and _foo is defined) 624280461Sdim *kind = pointer32; 625303239Sdim if (auto ec = atomFromAddress(0, reloc.value, target, addend)) 626280461Sdim return ec; 627280461Sdim *addend += (clearThumbBit(instruction, *target) - reloc.value); 628314564Sdim return llvm::Error::success(); 629280461Sdim default: 630303239Sdim return llvm::make_error<GenericError>("unsupported arm relocation type"); 631280461Sdim } 632314564Sdim return llvm::Error::success(); 633280461Sdim} 634280461Sdim 635303239Sdimllvm::Error 636280461SdimArchHandler_arm::getPairReferenceInfo(const normalized::Relocation &reloc1, 637280461Sdim const normalized::Relocation &reloc2, 638280461Sdim const DefinedAtom *inAtom, 639280461Sdim uint32_t offsetInAtom, 640280461Sdim uint64_t fixupAddress, bool isBig, 641280461Sdim bool scatterable, 642280461Sdim FindAtomBySectionAndAddress atomFromAddr, 643280461Sdim FindAtomBySymbolIndex atomFromSymbolIndex, 644280461Sdim Reference::KindValue *kind, 645280461Sdim const lld::Atom **target, 646280461Sdim Reference::Addend *addend) { 647280461Sdim bool pointerDiff = false; 648280461Sdim bool funcRel; 649280461Sdim bool top; 650280461Sdim bool thumbReloc; 651280461Sdim switch(relocPattern(reloc1) << 16 | relocPattern(reloc2)) { 652280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbLo) << 16 | 653280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbLo): 654280461Sdim // ex: movw r1, :lower16:(_x-L1) [thumb mode] 655280461Sdim *kind = thumb_movw_funcRel; 656280461Sdim funcRel = true; 657280461Sdim top = false; 658280461Sdim thumbReloc = true; 659280461Sdim break; 660280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenThmbHi) << 16 | 661280461Sdim ARM_RELOC_PAIR | rScattered | rLenThmbHi): 662280461Sdim // ex: movt r1, :upper16:(_x-L1) [thumb mode] 663280461Sdim *kind = thumb_movt_funcRel; 664280461Sdim funcRel = true; 665280461Sdim top = true; 666280461Sdim thumbReloc = true; 667280461Sdim break; 668280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmLo) << 16 | 669280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmLo): 670280461Sdim // ex: movw r1, :lower16:(_x-L1) [arm mode] 671280461Sdim *kind = arm_movw_funcRel; 672280461Sdim funcRel = true; 673280461Sdim top = false; 674280461Sdim thumbReloc = false; 675280461Sdim break; 676280461Sdim case ((ARM_RELOC_HALF_SECTDIFF | rScattered | rLenArmHi) << 16 | 677280461Sdim ARM_RELOC_PAIR | rScattered | rLenArmHi): 678280461Sdim // ex: movt r1, :upper16:(_x-L1) [arm mode] 679280461Sdim *kind = arm_movt_funcRel; 680280461Sdim funcRel = true; 681280461Sdim top = true; 682280461Sdim thumbReloc = false; 683280461Sdim break; 684280461Sdim case ((ARM_RELOC_HALF | rLenThmbLo) << 16 | 685280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 686280461Sdim // ex: movw r1, :lower16:_x [thumb mode] 687280461Sdim *kind = thumb_movw; 688280461Sdim funcRel = false; 689280461Sdim top = false; 690280461Sdim thumbReloc = true; 691280461Sdim break; 692280461Sdim case ((ARM_RELOC_HALF | rLenThmbHi) << 16 | 693280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 694280461Sdim // ex: movt r1, :upper16:_x [thumb mode] 695280461Sdim *kind = thumb_movt; 696280461Sdim funcRel = false; 697280461Sdim top = true; 698280461Sdim thumbReloc = true; 699280461Sdim break; 700280461Sdim case ((ARM_RELOC_HALF | rLenArmLo) << 16 | 701280461Sdim ARM_RELOC_PAIR | rLenArmLo): 702280461Sdim // ex: movw r1, :lower16:_x [arm mode] 703280461Sdim *kind = arm_movw; 704280461Sdim funcRel = false; 705280461Sdim top = false; 706280461Sdim thumbReloc = false; 707280461Sdim break; 708280461Sdim case ((ARM_RELOC_HALF | rLenArmHi) << 16 | 709280461Sdim ARM_RELOC_PAIR | rLenArmHi): 710280461Sdim // ex: movt r1, :upper16:_x [arm mode] 711280461Sdim *kind = arm_movt; 712280461Sdim funcRel = false; 713280461Sdim top = true; 714280461Sdim thumbReloc = false; 715280461Sdim break; 716280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenThmbLo) << 16 | 717280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 718280461Sdim // ex: movw r1, :lower16:_x+a [thumb mode] 719280461Sdim *kind = thumb_movw; 720280461Sdim funcRel = false; 721280461Sdim top = false; 722280461Sdim thumbReloc = true; 723280461Sdim break; 724280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenThmbHi) << 16 | 725280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 726280461Sdim // ex: movt r1, :upper16:_x+a [thumb mode] 727280461Sdim *kind = thumb_movt; 728280461Sdim funcRel = false; 729280461Sdim top = true; 730280461Sdim thumbReloc = true; 731280461Sdim break; 732280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenArmLo) << 16 | 733280461Sdim ARM_RELOC_PAIR | rLenArmLo): 734280461Sdim // ex: movw r1, :lower16:_x+a [arm mode] 735280461Sdim *kind = arm_movw; 736280461Sdim funcRel = false; 737280461Sdim top = false; 738280461Sdim thumbReloc = false; 739280461Sdim break; 740280461Sdim case ((ARM_RELOC_HALF | rScattered | rLenArmHi) << 16 | 741280461Sdim ARM_RELOC_PAIR | rLenArmHi): 742280461Sdim // ex: movt r1, :upper16:_x+a [arm mode] 743280461Sdim *kind = arm_movt; 744280461Sdim funcRel = false; 745280461Sdim top = true; 746280461Sdim thumbReloc = false; 747280461Sdim break; 748280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenThmbLo) << 16 | 749280461Sdim ARM_RELOC_PAIR | rLenThmbLo): 750280461Sdim // ex: movw r1, :lower16:_undef [thumb mode] 751280461Sdim *kind = thumb_movw; 752280461Sdim funcRel = false; 753280461Sdim top = false; 754280461Sdim thumbReloc = true; 755280461Sdim break; 756280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenThmbHi) << 16 | 757280461Sdim ARM_RELOC_PAIR | rLenThmbHi): 758280461Sdim // ex: movt r1, :upper16:_undef [thumb mode] 759280461Sdim *kind = thumb_movt; 760280461Sdim funcRel = false; 761280461Sdim top = true; 762280461Sdim thumbReloc = true; 763280461Sdim break; 764280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenArmLo) << 16 | 765280461Sdim ARM_RELOC_PAIR | rLenArmLo): 766280461Sdim // ex: movw r1, :lower16:_undef [arm mode] 767280461Sdim *kind = arm_movw; 768280461Sdim funcRel = false; 769280461Sdim top = false; 770280461Sdim thumbReloc = false; 771280461Sdim break; 772280461Sdim case ((ARM_RELOC_HALF | rExtern | rLenArmHi) << 16 | 773280461Sdim ARM_RELOC_PAIR | rLenArmHi): 774280461Sdim // ex: movt r1, :upper16:_undef [arm mode] 775280461Sdim *kind = arm_movt; 776280461Sdim funcRel = false; 777280461Sdim top = true; 778280461Sdim thumbReloc = false; 779280461Sdim break; 780280461Sdim case ((ARM_RELOC_SECTDIFF | rScattered | rLength4) << 16 | 781280461Sdim ARM_RELOC_PAIR | rScattered | rLength4): 782280461Sdim case ((ARM_RELOC_LOCAL_SECTDIFF | rScattered | rLength4) << 16 | 783280461Sdim ARM_RELOC_PAIR | rScattered | rLength4): 784280461Sdim // ex: .long _foo - . 785280461Sdim pointerDiff = true; 786280461Sdim break; 787280461Sdim default: 788303239Sdim return llvm::make_error<GenericError>("unsupported arm relocation pair"); 789280461Sdim } 790280461Sdim const uint8_t *fixupContent = &inAtom->rawContent()[offsetInAtom]; 791280461Sdim uint32_t instruction = *(const ulittle32_t *)fixupContent; 792280461Sdim uint32_t value; 793280461Sdim uint32_t fromAddress; 794280461Sdim uint32_t toAddress; 795280461Sdim uint16_t instruction16; 796280461Sdim uint16_t other16; 797280461Sdim const lld::Atom *fromTarget; 798280461Sdim Reference::Addend offsetInTo; 799280461Sdim Reference::Addend offsetInFrom; 800280461Sdim if (pointerDiff) { 801280461Sdim toAddress = reloc1.value; 802280461Sdim fromAddress = reloc2.value; 803303239Sdim if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) 804280461Sdim return ec; 805303239Sdim if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) 806280461Sdim return ec; 807280461Sdim if (scatterable && (fromTarget != inAtom)) 808303239Sdim return llvm::make_error<GenericError>( 809292934Sdim "SECTDIFF relocation where subtrahend label is not in atom"); 810280461Sdim *kind = delta32; 811280461Sdim value = clearThumbBit(instruction, *target); 812280461Sdim *addend = (int32_t)(value - (toAddress - fixupAddress)); 813280461Sdim } else if (funcRel) { 814280461Sdim toAddress = reloc1.value; 815280461Sdim fromAddress = reloc2.value; 816303239Sdim if (auto ec = atomFromAddr(0, toAddress, target, &offsetInTo)) 817280461Sdim return ec; 818303239Sdim if (auto ec = atomFromAddr(0, fromAddress, &fromTarget, &offsetInFrom)) 819280461Sdim return ec; 820280461Sdim if (fromTarget != inAtom) 821303239Sdim return llvm::make_error<GenericError>("ARM_RELOC_HALF_SECTDIFF relocation" 822303239Sdim " where subtrahend label is not in atom"); 823280461Sdim other16 = (reloc2.offset & 0xFFFF); 824280461Sdim if (thumbReloc) { 825280461Sdim if (top) { 826280461Sdim if (!isThumbMovt(instruction)) 827303239Sdim return llvm::make_error<GenericError>("expected movt instruction"); 828280461Sdim } 829280461Sdim else { 830280461Sdim if (!isThumbMovw(instruction)) 831303239Sdim return llvm::make_error<GenericError>("expected movw instruction"); 832280461Sdim } 833280461Sdim instruction16 = getWordFromThumbMov(instruction); 834280461Sdim } 835280461Sdim else { 836280461Sdim if (top) { 837280461Sdim if (!isArmMovt(instruction)) 838303239Sdim return llvm::make_error<GenericError>("expected movt instruction"); 839280461Sdim } 840280461Sdim else { 841280461Sdim if (!isArmMovw(instruction)) 842303239Sdim return llvm::make_error<GenericError>("expected movw instruction"); 843280461Sdim } 844280461Sdim instruction16 = getWordFromArmMov(instruction); 845280461Sdim } 846280461Sdim if (top) 847280461Sdim value = (instruction16 << 16) | other16; 848280461Sdim else 849280461Sdim value = (other16 << 16) | instruction16; 850280461Sdim value = clearThumbBit(value, *target); 851280461Sdim int64_t ta = (int64_t) value - (toAddress - fromAddress); 852280461Sdim *addend = ta - offsetInFrom; 853314564Sdim return llvm::Error::success(); 854280461Sdim } else { 855280461Sdim uint32_t sectIndex; 856280461Sdim if (thumbReloc) { 857280461Sdim if (top) { 858280461Sdim if (!isThumbMovt(instruction)) 859303239Sdim return llvm::make_error<GenericError>("expected movt instruction"); 860280461Sdim } 861280461Sdim else { 862280461Sdim if (!isThumbMovw(instruction)) 863303239Sdim return llvm::make_error<GenericError>("expected movw instruction"); 864280461Sdim } 865280461Sdim instruction16 = getWordFromThumbMov(instruction); 866280461Sdim } 867280461Sdim else { 868280461Sdim if (top) { 869280461Sdim if (!isArmMovt(instruction)) 870303239Sdim return llvm::make_error<GenericError>("expected movt instruction"); 871280461Sdim } 872280461Sdim else { 873280461Sdim if (!isArmMovw(instruction)) 874303239Sdim return llvm::make_error<GenericError>("expected movw instruction"); 875280461Sdim } 876280461Sdim instruction16 = getWordFromArmMov(instruction); 877280461Sdim } 878280461Sdim other16 = (reloc2.offset & 0xFFFF); 879280461Sdim if (top) 880280461Sdim value = (instruction16 << 16) | other16; 881280461Sdim else 882280461Sdim value = (other16 << 16) | instruction16; 883280461Sdim if (reloc1.isExtern) { 884303239Sdim if (auto ec = atomFromSymbolIndex(reloc1.symbol, target)) 885280461Sdim return ec; 886280461Sdim *addend = value; 887280461Sdim } else { 888280461Sdim if (reloc1.scattered) { 889280461Sdim toAddress = reloc1.value; 890280461Sdim sectIndex = 0; 891280461Sdim } else { 892280461Sdim toAddress = value; 893280461Sdim sectIndex = reloc1.symbol; 894280461Sdim } 895303239Sdim if (auto ec = atomFromAddr(sectIndex, toAddress, target, &offsetInTo)) 896280461Sdim return ec; 897280461Sdim *addend = value - toAddress; 898280461Sdim } 899280461Sdim } 900280461Sdim 901314564Sdim return llvm::Error::success(); 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, 1017303239Sdim llvm::MutableArrayRef<uint8_t> atomContentBuffer) { 1018280461Sdim // Copy raw bytes. 1019303239Sdim std::copy(atom.rawContent().begin(), atom.rawContent().end(), 1020303239Sdim atomContentBuffer.begin()); 1021280461Sdim // Apply fix-ups. 1022280461Sdim bool thumbMode = false; 1023280461Sdim for (const Reference *ref : atom) { 1024280461Sdim uint32_t offset = ref->offsetInAtom(); 1025280461Sdim const Atom *target = ref->target(); 1026280461Sdim uint64_t targetAddress = 0; 1027280461Sdim bool targetIsThumb = false; 1028280461Sdim if (const DefinedAtom *defTarg = dyn_cast<DefinedAtom>(target)) { 1029280461Sdim targetAddress = findAddress(*target); 1030280461Sdim targetIsThumb = isThumbFunction(*defTarg); 1031280461Sdim } 1032280461Sdim uint64_t atomAddress = findAddress(atom); 1033280461Sdim uint64_t fixupAddress = atomAddress + offset; 1034280461Sdim if (relocatable) { 1035280461Sdim applyFixupRelocatable(*ref, &atomContentBuffer[offset], fixupAddress, 1036280461Sdim targetAddress, atomAddress, thumbMode, 1037280461Sdim targetIsThumb); 1038280461Sdim } else { 1039280461Sdim applyFixupFinal(*ref, &atomContentBuffer[offset], fixupAddress, 1040280461Sdim targetAddress, atomAddress, thumbMode, targetIsThumb); 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()) { 1396303239Sdim atom.addReference(Reference::KindNamespace::mach_o, 1397303239Sdim Reference::KindArch::ARM, modeThumbCode, 0, &atom, 0); 1398280461Sdim } 1399280461Sdim} 1400280461Sdim 1401280461Sdimbool ArchHandler_arm::isThumbFunction(const DefinedAtom &atom) { 1402280461Sdim for (const Reference *ref : atom) { 1403280461Sdim if (ref->offsetInAtom() != 0) 1404280461Sdim return false; 1405280461Sdim if (ref->kindNamespace() != Reference::KindNamespace::mach_o) 1406280461Sdim continue; 1407280461Sdim assert(ref->kindArch() == Reference::KindArch::ARM); 1408280461Sdim if (ref->kindValue() == modeThumbCode) 1409280461Sdim return true; 1410280461Sdim } 1411280461Sdim return false; 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 1428303239Sdim ~Thumb2ToArmShimAtom() override = default; 1429303239Sdim 1430280461Sdim StringRef name() const override { 1431280461Sdim return _name; 1432280461Sdim } 1433280461Sdim 1434280461Sdim ContentType contentType() const override { 1435280461Sdim return DefinedAtom::typeCode; 1436280461Sdim } 1437280461Sdim 1438292934Sdim Alignment alignment() const override { return 4; } 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 1461280461Sdimclass ArmToThumbShimAtom : public SimpleDefinedAtom { 1462280461Sdimpublic: 1463280461Sdim ArmToThumbShimAtom(MachOFile &file, StringRef targetName, 1464280461Sdim const DefinedAtom &target) 1465280461Sdim : SimpleDefinedAtom(file) { 1466280461Sdim addReference(Reference::KindNamespace::mach_o, Reference::KindArch::ARM, 1467280461Sdim ArchHandler_arm::delta32, 12, &target, 0); 1468280461Sdim std::string name = std::string(targetName) + "$shim"; 1469280461Sdim StringRef tmp(name); 1470280461Sdim _name = tmp.copy(file.allocator()); 1471280461Sdim } 1472280461Sdim 1473303239Sdim ~ArmToThumbShimAtom() override = default; 1474303239Sdim 1475280461Sdim StringRef name() const override { 1476280461Sdim return _name; 1477280461Sdim } 1478280461Sdim 1479280461Sdim ContentType contentType() const override { 1480280461Sdim return DefinedAtom::typeCode; 1481280461Sdim } 1482280461Sdim 1483292934Sdim Alignment alignment() const override { return 4; } 1484280461Sdim 1485280461Sdim uint64_t size() const override { 1486280461Sdim return 16; 1487280461Sdim } 1488280461Sdim 1489280461Sdim ContentPermissions permissions() const override { 1490280461Sdim return DefinedAtom::permR_X; 1491280461Sdim } 1492280461Sdim 1493280461Sdim ArrayRef<uint8_t> rawContent() const override { 1494280461Sdim static const uint8_t bytes[] = 1495280461Sdim { 0x04, 0xC0, 0x9F, 0xE5, // ldr ip, pc + 4 1496280461Sdim 0x0C, 0xC0, 0x8F, 0xE0, // add ip, pc, ip 1497280461Sdim 0x1C, 0xFF, 0x2F, 0xE1, // ldr pc, [ip] 1498280461Sdim 0x00, 0x00, 0x00, 0x00 }; // .long target - this 1499280461Sdim assert(sizeof(bytes) == size()); 1500280461Sdim return llvm::makeArrayRef(bytes, sizeof(bytes)); 1501280461Sdim } 1502280461Sdimprivate: 1503280461Sdim StringRef _name; 1504280461Sdim}; 1505280461Sdim 1506280461Sdimconst DefinedAtom *ArchHandler_arm::createShim(MachOFile &file, 1507280461Sdim bool thumbToArm, 1508280461Sdim const DefinedAtom &target) { 1509280461Sdim bool isStub = (target.contentType() == DefinedAtom::typeStub); 1510280461Sdim StringRef targetName = isStub ? stubName(target) : target.name(); 1511280461Sdim if (thumbToArm) 1512280461Sdim return new (file.allocator()) Thumb2ToArmShimAtom(file, targetName, target); 1513280461Sdim else 1514280461Sdim return new (file.allocator()) ArmToThumbShimAtom(file, targetName, target); 1515280461Sdim} 1516280461Sdim 1517280461Sdimstd::unique_ptr<mach_o::ArchHandler> ArchHandler::create_arm() { 1518280461Sdim return std::unique_ptr<mach_o::ArchHandler>(new ArchHandler_arm()); 1519280461Sdim} 1520280461Sdim 1521280461Sdim} // namespace mach_o 1522280461Sdim} // namespace lld 1523