153 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "Arch/ARM64Common.h"10 11#include "lld/Common/ErrorHandler.h"12#include "llvm/Support/Endian.h"13 14using namespace llvm::MachO;15using namespace llvm::support::endian;16using namespace lld;17using namespace lld::macho;18 19int64_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 their24 // addends.25 // TODO(gkm): extract embedded addend just so we can assert that it is 026 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 41static 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 base56// instruction is pre-populated in the text section. A pre-populated57// instruction has opcode & register-operand bits set, with immediate58// operands zeroed. We read it from text, OR-in the immediate59// operands, then write-back the completed instruction.60void 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 94void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {95 // The instruction format comments below are quoted from96 // Arm® Architecture Reference Manual97 // Armv8, for Armv8-A architecture profile98 // 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 113void 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 NOP122 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 131static 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 141void 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 149void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,150 uint64_t va, int align) {151 ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);152}153