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 static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) { 42 switch (r.length) { 43 case 2: 44 checkInt(loc, r, value, 32); 45 write32le(loc, value); 46 break; 47 case 3: 48 write64le(loc, value); 49 break; 50 default: 51 llvm_unreachable("invalid r_length"); 52 } 53 } 54 55 // For instruction relocations (load, store, add), the base 56 // instruction is pre-populated in the text section. A pre-populated 57 // instruction has opcode & register-operand bits set, with immediate 58 // operands zeroed. We read it from text, OR-in the immediate 59 // operands, then write-back the completed instruction. 60 void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, 61 uint64_t pc) const { 62 auto loc32 = reinterpret_cast<uint32_t *>(loc); 63 uint32_t base = ((r.length == 2) ? read32le(loc) : 0); 64 switch (r.type) { 65 case ARM64_RELOC_BRANCH26: 66 encodeBranch26(loc32, r, base, value - pc); 67 break; 68 case ARM64_RELOC_SUBTRACTOR: 69 case ARM64_RELOC_UNSIGNED: 70 writeValue(loc, r, value); 71 break; 72 case ARM64_RELOC_POINTER_TO_GOT: 73 if (r.pcrel) 74 value -= pc; 75 writeValue(loc, r, value); 76 break; 77 case ARM64_RELOC_PAGE21: 78 case ARM64_RELOC_GOT_LOAD_PAGE21: 79 case ARM64_RELOC_TLVP_LOAD_PAGE21: 80 assert(r.pcrel); 81 encodePage21(loc32, r, base, pageBits(value) - pageBits(pc)); 82 break; 83 case ARM64_RELOC_PAGEOFF12: 84 case ARM64_RELOC_GOT_LOAD_PAGEOFF12: 85 case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: 86 assert(!r.pcrel); 87 encodePageOff12(loc32, r, base, value); 88 break; 89 default: 90 llvm_unreachable("unexpected relocation type"); 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 113 void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r, 114 uint8_t *loc) const { 115 assert(r.type == ARM64_RELOC_BRANCH26); 116 117 if (config->outputType == MH_OBJECT) 118 return; 119 120 if (sym->getName().starts_with("___dtrace_probe")) { 121 // change call site to a NOP 122 write32le(loc, 0xD503201F); 123 } else if (sym->getName().starts_with("___dtrace_isenabled")) { 124 // change call site to 'MOVZ X0,0' 125 write32le(loc, 0xD2800000); 126 } else { 127 error("Unrecognized dtrace symbol prefix: " + toString(*sym)); 128 } 129 } 130 131 static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align, 132 const Symbol *sym) { 133 std::string symbolHint; 134 if (sym) 135 symbolHint = " (" + toString(*sym) + ")"; 136 error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" + 137 llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) + 138 "-byte aligned"); 139 } 140 141 void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r, 142 uint64_t va, int align) { 143 uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart; 144 const InputSection *isec = offsetToInputSection(&off); 145 std::string locStr = isec ? isec->getLocation(off) : "(invalid location)"; 146 ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>()); 147 } 148 149 void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d, 150 uint64_t va, int align) { 151 ::reportUnalignedLdrStr(d.reason, va, align, d.symbol); 152 } 153