This fixes https://github.com/llvm/llvm-project/issues/56238. ld64.lld currently does not generate __dof section in Mach-O, and -no_dtrace_dof option is on by default. However when there are user-defined dtrace symbols, ld64.lld will treat them as undefined symbols, which causes the linking to fail because lld cannot find their definitions. This patch allows ld64.lld to rewrite the instructions calling dtrace symbols to instructions like nop as what ld64 does; therefore, when encountered with user-provided dtrace probes, the linking can still succeed. I'm not sure whether support for dtrace is expected in lld, so for now I didn't add codes to make lld emit __dof section like ld64, and only made it possible to link with dtrace symbols provided. If this feature is needed, I can add that part in Dtrace.cpp & Dtrace.h. Reviewed By: int3, #lld-macho Differential Revision: https://reviews.llvm.org/D129062
130 lines
4.2 KiB
C++
130 lines
4.2 KiB
C++
//===- 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, 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));
|
|
}
|
|
}
|