| //===- ARM64Common.cpp ----------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Arch/ARM64Common.h" |
| |
| #include "lld/Common/ErrorHandler.h" |
| #include "llvm/Support/Endian.h" |
| |
| using namespace llvm::MachO; |
| using namespace llvm::support::endian; |
| using namespace lld; |
| using namespace lld::macho; |
| |
| int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset, |
| const relocation_info rel) const { |
| if (rel.r_type != ARM64_RELOC_UNSIGNED && |
| rel.r_type != ARM64_RELOC_SUBTRACTOR) { |
| // All other reloc types should use the ADDEND relocation to store their |
| // addends. |
| // TODO(gkm): extract embedded addend just so we can assert that it is 0 |
| return 0; |
| } |
| |
| const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart()); |
| const uint8_t *loc = buf + offset + rel.r_address; |
| switch (rel.r_length) { |
| case 2: |
| return static_cast<int32_t>(read32le(loc)); |
| case 3: |
| return read64le(loc); |
| default: |
| llvm_unreachable("invalid r_length"); |
| } |
| } |
| |
| static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) { |
| switch (r.length) { |
| case 2: |
| checkInt(loc, r, value, 32); |
| write32le(loc, value); |
| break; |
| case 3: |
| write64le(loc, value); |
| break; |
| default: |
| llvm_unreachable("invalid r_length"); |
| } |
| } |
| |
| // For instruction relocations (load, store, add), the base |
| // instruction is pre-populated in the text section. A pre-populated |
| // instruction has opcode & register-operand bits set, with immediate |
| // operands zeroed. We read it from text, OR-in the immediate |
| // operands, then write-back the completed instruction. |
| void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value, |
| uint64_t pc) const { |
| auto loc32 = reinterpret_cast<uint32_t *>(loc); |
| uint32_t base = ((r.length == 2) ? read32le(loc) : 0); |
| switch (r.type) { |
| case ARM64_RELOC_BRANCH26: |
| encodeBranch26(loc32, r, base, value - pc); |
| break; |
| case ARM64_RELOC_SUBTRACTOR: |
| case ARM64_RELOC_UNSIGNED: |
| writeValue(loc, r, value); |
| break; |
| case ARM64_RELOC_POINTER_TO_GOT: |
| if (r.pcrel) |
| value -= pc; |
| writeValue(loc, r, value); |
| break; |
| case ARM64_RELOC_PAGE21: |
| case ARM64_RELOC_GOT_LOAD_PAGE21: |
| case ARM64_RELOC_TLVP_LOAD_PAGE21: |
| assert(r.pcrel); |
| encodePage21(loc32, r, base, pageBits(value) - pageBits(pc)); |
| break; |
| case ARM64_RELOC_PAGEOFF12: |
| case ARM64_RELOC_GOT_LOAD_PAGEOFF12: |
| case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: |
| assert(!r.pcrel); |
| encodePageOff12(loc32, r, base, value); |
| break; |
| default: |
| llvm_unreachable("unexpected relocation type"); |
| } |
| } |
| |
| void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const { |
| // The instruction format comments below are quoted from |
| // ArmĀ® Architecture Reference Manual |
| // Armv8, for Armv8-A architecture profile |
| // ARM DDI 0487G.a (ID011921) |
| uint32_t instruction = read32le(loc); |
| // C6.2.132 LDR (immediate) |
| // This matches both the 64- and 32-bit variants: |
| // LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}] |
| if ((instruction & 0xbfc00000) != 0xb9400000) |
| error(getRelocAttrs(type).name + " reloc requires LDR instruction"); |
| assert(((instruction >> 10) & 0xfff) == 0 && |
| "non-zero embedded LDR immediate"); |
| // C6.2.4 ADD (immediate) |
| // ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>} |
| instruction = ((instruction & 0x001fffff) | 0x91000000); |
| write32le(loc, instruction); |
| } |
| |
| void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r, |
| uint8_t *loc) const { |
| assert(r.type == ARM64_RELOC_BRANCH26); |
| |
| if (config->outputType == MH_OBJECT) |
| return; |
| |
| if (sym->getName().startswith("___dtrace_probe")) { |
| // change call site to a NOP |
| write32le(loc, 0xD503201F); |
| } else if (sym->getName().startswith("___dtrace_isenabled")) { |
| // change call site to 'MOVZ X0,0' |
| write32le(loc, 0xD2800000); |
| } else { |
| error("Unrecognized dtrace symbol prefix: " + toString(*sym)); |
| } |
| } |
| |
| static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align, |
| const Symbol *sym) { |
| std::string symbolHint; |
| if (sym) |
| symbolHint = " (" + toString(*sym) + ")"; |
| error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" + |
| llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) + |
| "-byte aligned"); |
| } |
| |
| void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r, |
| uint64_t va, int align) { |
| uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart; |
| const InputSection *isec = offsetToInputSection(&off); |
| std::string locStr = isec ? isec->getLocation(off) : "(invalid location)"; |
| ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>()); |
| } |
| |
| void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d, |
| uint64_t va, int align) { |
| ::reportUnalignedLdrStr(d.reason, va, align, d.symbol); |
| } |