Path: blob/main/contrib/llvm-project/lld/MachO/Arch/ARM64Common.cpp
34889 views
//===- ARM64Common.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Arch/ARM64Common.h"910#include "lld/Common/ErrorHandler.h"11#include "llvm/Support/Endian.h"1213using namespace llvm::MachO;14using namespace llvm::support::endian;15using namespace lld;16using namespace lld::macho;1718int64_t ARM64Common::getEmbeddedAddend(MemoryBufferRef mb, uint64_t offset,19const relocation_info rel) const {20if (rel.r_type != ARM64_RELOC_UNSIGNED &&21rel.r_type != ARM64_RELOC_SUBTRACTOR) {22// All other reloc types should use the ADDEND relocation to store their23// addends.24// TODO(gkm): extract embedded addend just so we can assert that it is 025return 0;26}2728const auto *buf = reinterpret_cast<const uint8_t *>(mb.getBufferStart());29const uint8_t *loc = buf + offset + rel.r_address;30switch (rel.r_length) {31case 2:32return static_cast<int32_t>(read32le(loc));33case 3:34return read64le(loc);35default:36llvm_unreachable("invalid r_length");37}38}3940static void writeValue(uint8_t *loc, const Reloc &r, uint64_t value) {41switch (r.length) {42case 2:43checkInt(loc, r, value, 32);44write32le(loc, value);45break;46case 3:47write64le(loc, value);48break;49default:50llvm_unreachable("invalid r_length");51}52}5354// For instruction relocations (load, store, add), the base55// instruction is pre-populated in the text section. A pre-populated56// instruction has opcode & register-operand bits set, with immediate57// operands zeroed. We read it from text, OR-in the immediate58// operands, then write-back the completed instruction.59void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,60uint64_t pc) const {61auto loc32 = reinterpret_cast<uint32_t *>(loc);62uint32_t base = ((r.length == 2) ? read32le(loc) : 0);63switch (r.type) {64case ARM64_RELOC_BRANCH26:65encodeBranch26(loc32, r, base, value - pc);66break;67case ARM64_RELOC_SUBTRACTOR:68case ARM64_RELOC_UNSIGNED:69writeValue(loc, r, value);70break;71case ARM64_RELOC_POINTER_TO_GOT:72if (r.pcrel)73value -= pc;74writeValue(loc, r, value);75break;76case ARM64_RELOC_PAGE21:77case ARM64_RELOC_GOT_LOAD_PAGE21:78case ARM64_RELOC_TLVP_LOAD_PAGE21:79assert(r.pcrel);80encodePage21(loc32, r, base, pageBits(value) - pageBits(pc));81break;82case ARM64_RELOC_PAGEOFF12:83case ARM64_RELOC_GOT_LOAD_PAGEOFF12:84case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:85assert(!r.pcrel);86encodePageOff12(loc32, r, base, value);87break;88default:89llvm_unreachable("unexpected relocation type");90}91}9293void ARM64Common::relaxGotLoad(uint8_t *loc, uint8_t type) const {94// The instruction format comments below are quoted from95// ArmĀ® Architecture Reference Manual96// Armv8, for Armv8-A architecture profile97// ARM DDI 0487G.a (ID011921)98uint32_t instruction = read32le(loc);99// C6.2.132 LDR (immediate)100// This matches both the 64- and 32-bit variants:101// LDR <(X|W)t>, [<Xn|SP>{, #<pimm>}]102if ((instruction & 0xbfc00000) != 0xb9400000)103error(getRelocAttrs(type).name + " reloc requires LDR instruction");104assert(((instruction >> 10) & 0xfff) == 0 &&105"non-zero embedded LDR immediate");106// C6.2.4 ADD (immediate)107// ADD <Xd|SP>, <Xn|SP>, #<imm>{, <shift>}108instruction = ((instruction & 0x001fffff) | 0x91000000);109write32le(loc, instruction);110}111112void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,113uint8_t *loc) const {114assert(r.type == ARM64_RELOC_BRANCH26);115116if (config->outputType == MH_OBJECT)117return;118119if (sym->getName().starts_with("___dtrace_probe")) {120// change call site to a NOP121write32le(loc, 0xD503201F);122} else if (sym->getName().starts_with("___dtrace_isenabled")) {123// change call site to 'MOVZ X0,0'124write32le(loc, 0xD2800000);125} else {126error("Unrecognized dtrace symbol prefix: " + toString(*sym));127}128}129130static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,131const Symbol *sym) {132std::string symbolHint;133if (sym)134symbolHint = " (" + toString(*sym) + ")";135error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +136llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +137"-byte aligned");138}139140void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,141uint64_t va, int align) {142uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;143const InputSection *isec = offsetToInputSection(&off);144std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";145::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());146}147148void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,149uint64_t va, int align) {150::reportUnalignedLdrStr(d.reason, va, align, d.symbol);151}152153154