| 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(P: loc)); |
| 34 | case 3: |
| 35 | return read64le(P: 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, d: r, v: value, bits: 32); |
| 45 | write32le(P: loc, V: value); |
| 46 | break; |
| 47 | case 3: |
| 48 | write64le(P: loc, V: 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(P: loc) : 0); |
| 64 | switch (r.type) { |
| 65 | case ARM64_RELOC_BRANCH26: |
| 66 | encodeBranch26(loc: loc32, r, base, va: 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(loc: loc32, r, base, va: pageBits(address: value) - pageBits(address: 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(loc: loc32, t: r, base, va: 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(P: 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(msg: 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(P: loc, V: 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(Prefix: "___dtrace_probe" )) { |
| 121 | // change call site to a NOP |
| 122 | write32le(P: loc, V: 0xD503201F); |
| 123 | } else if (sym->getName().starts_with(Prefix: "___dtrace_isenabled" )) { |
| 124 | // change call site to 'MOVZ X0,0' |
| 125 | write32le(P: loc, V: 0xD2800000); |
| 126 | } else { |
| 127 | error(msg: "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(msg: loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" + |
| 137 | llvm::utohexstr(X: 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(loc: locStr, va, align, sym: 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(loc: d.reason, va, align, sym: d.symbol); |
| 152 | } |
| 153 | |