1 //===- ARM64Common.cpp ----------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Arch/ARM64Common.h" 10 11 #include "lld/Common/ErrorHandler.h" 12 #include "llvm/Support/Endian.h" 13 14 using namespace llvm::MachO; 15 using namespace llvm::support::endian; 16 using namespace lld; 17 using namespace lld::macho; 18 19 int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, 20 const relocation_info rel) const { 21 if (rel.r_type != ARM64_RELOC_UNSIGNED && 22 rel.r_type != ARM64_RELOC_SUBTRACTOR) { 23 // All other reloc types should use the ADDEND relocation to store their 24 // addends. 25 // TODO(gkm): extract embedded addend just so we can assert that it is 0 26 return 0; 27 } 28 29 const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); 30 const uint8_t *loc = buf + offset + rel.r_address; 31 switch (rel.r_length) { 32 case 2: 33 return static_cast<int32_t>(read32le(loc)); 34 case 3: 35 return read64le(loc); 36 default: 37 llvm_unreachable("invalid r_length"); 38 } 39 } 40 41 // For instruction relocations (load, store, add), the base 42 // instruction is pre-populated in the text section. A pre-populated 43 // instruction has opcode & register-operand bits set, with immediate 44 // operands zeroed. We read it from text, OR-in the immediate 45 // operands, then write-back the completed instruction. 46 47 void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, 48 uint64_t pc) const { 49 uint32_t base = ((r.length == 2) ? read32le(loc) : 0); 50 value += r.addend; 51 switch (r.type) { 52 case ARM64_RELOC_BRANCH26: 53 value = encodeBranch26(r, base, value - pc); 54 break; 55 case ARM64_RELOC_SUBTRACTOR: 56 case ARM64_RELOC_UNSIGNED: 57 if (r.length == 2) 58 checkInt(r, value, 32); 59 break; 60 case ARM64_RELOC_POINTER_TO_GOT: 61 if (r.pcrel) 62 value -= pc; 63 checkInt(r, value, 32); 64 break; 65 case ARM64_RELOC_PAGE21: 66 case ARM64_RELOC_GOT_LOAD_PAGE21: 67 case ARM64_RELOC_TLVP_LOAD_PAGE21: { 68 assert(r.pcrel); 69 value = encodePage21(r, base, pageBits(value) - pageBits(pc)); 70 break; 71 } 72 case ARM64_RELOC_PAGEOFF12: 73 case ARM64_RELOC_GOT_LOAD_PAGEOFF12: 74 case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: 75 assert(!r.pcrel); 76 value = encodePageOff12(base, value); 77 break; 78 default: 79 llvm_unreachable("unexpected relocation type"); 80 } 81 82 switch (r.length) { 83 case 2: 84 write32le(loc, value); 85 break; 86 case 3: 87 write64le(loc, value); 88 break; 89 default: 90 llvm_unreachable("invalid r_length"); 91 } 92 } 93 94 void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { 95 // The instruction format comments below are quoted from 96 // Arm® Architecture Reference Manual 97 // Armv8, for Armv8-A architecture profile 98 // ARM DDI 0487G.a (ID011921) 99 uint32_t instruction = read32le(loc); 100 // C6.2.132 LDR (immediate) 101 // This matches both the 64- and 32-bit variants: 102 // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}] 103 if ((instruction & 0xbfc00000) != 0xb9400000) 104 error(getRelocAttrs(type).name + " reloc requires LDR instruction"); 105 assert(((instruction >> 10) & 0xfff) == 0 && 106 "non-zero embedded LDR immediate"); 107 // C6.2.4 ADD (immediate) 108 // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>} 109 instruction = ((instruction & 0x001fffff) | 0x91000000); 110 write32le(loc, instruction); 111 } 112