1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
//===- 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().starts_with("___dtrace_probe")) {
// change call site to a NOP
write32le(loc, 0xD503201F);
} else if (sym->getName().starts_with("___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);
}
|