1
1
mirror of https://github.com/rui314/mold.git synced 2024-09-21 09:57:18 +03:00
mold/macho/arch-arm64.cc
Rui Ueyama d8e25b9753 wip
2021-12-04 13:17:47 +09:00

268 lines
7.0 KiB
C++

#include "mold.h"
#include <algorithm>
namespace mold::macho {
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 encode_page(u64 val) {
return (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
}
template <>
void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++) {
static const u32 insn[] = {
0x90000010, // adrp x16, $ptr@PAGE
0xf9400210, // ldr x16, [x16, $ptr@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn) == ARM64::stub_size);
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + ARM64::wordsize * i;
u64 this_addr = this->hdr.addr + ARM64::stub_size * i;
memcpy(buf, insn, sizeof(insn));
buf[0] |= encode_page(page(la_addr) - page(this_addr));
buf[1] |= bits(la_addr, 11, 3) << 10;
buf += 3;
}
}
template <>
void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *start = (u32 *)(ctx.buf + this->hdr.offset);
u32 *buf = start;
static const u32 insn0[] = {
0x90000011, // adrp x17, $_dyld_private@PAGE
0x91000231, // add x17, x17, $_dyld_private@PAGEOFF
0xa9bf47f0, // stp x16, x17, [sp, #-16]!
0x90000010, // adrp x16, $dyld_stub_binder@PAGE
0xf9400210, // ldr x16, [x16, $dyld_stub_binder@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn0) == ARM64::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
u64 dyld_private = intern(ctx, "__dyld_private")->get_addr(ctx);
buf[0] |= encode_page(page(dyld_private) - page(this->hdr.addr));
buf[1] |= bits(dyld_private, 11, 0) << 10;
u64 stub_binder = intern(ctx, "dyld_stub_binder")->get_addr(ctx);
buf[3] |= encode_page(page(stub_binder) - page(this->hdr.addr - 12));
buf[4] |= bits(stub_binder, 11, 0) << 10;
buf += 6;
for (i64 i = 0; i < ctx.stubs.syms.size(); i++) {
static const u32 insn[] = {
0x18000050, // ldr w16, addr
0x14000000, // b stubHelperHeader
0x00000000, // addr: .long <idx>
};
static_assert(sizeof(insn) == ARM64::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
buf[1] |= bits((start - buf - 1) * 4, 27, 2);
buf[2] = ctx.stubs.bind_offsets[i];
buf += 3;
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
switch (r.p2size) {
case 2:
return *(i32 *)(buf + r.offset);
case 3:
return *(i64 *)(buf + r.offset);
default:
unreachable();
}
}
static Relocation<ARM64>
read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr, MachRel *rels, i64 &idx) {
i64 addend = 0;
switch (rels[idx].type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
addend = read_addend((u8 *)file.mf->data + hdr.offset, rels[idx]);
break;
case ARM64_RELOC_ADDEND:
addend = rels[idx++].offset;
break;
}
MachRel &r = rels[idx];
Relocation<ARM64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + addend;
else
addr = addend;
Subsection<ARM64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<ARM64>>
read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr) {
std::vector<Relocation<ARM64>> vec;
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels, i));
return vec;
}
template <>
void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
for (Relocation<ARM64> &r : get_rels()) {
Symbol<ARM64> *sym = r.sym;
if (!sym)
continue;
switch (r.type) {
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
sym->flags |= NEEDS_GOT;
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib) {
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
std::span<Relocation<ARM64>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<ARM64> &r = rels[i];
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_BRANCH26:
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_PAGEOFF12:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case ARM64_RELOC_SUBTRACTOR: {
Relocation<ARM64> s = rels[++i];
assert(s.type == ARM64_RELOC_UNSIGNED);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val = val2 - val1;
break;
}
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
val = r.sym->get_got_addr(ctx);
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
val = r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
case ARM64_RELOC_POINTER_TO_GOT:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
switch (r.p2size) {
case 2:
*(i32 *)(buf + r.offset) = val;
break;
case 3:
*(i64 *)(buf + r.offset) = val;
break;
default:
unreachable();
}
break;
case ARM64_RELOC_BRANCH26:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
*(u32 *)(buf + r.offset) |= bits(val, 27, 2);
break;
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGE21:
assert(r.is_pcrel);
*(u32 *)(buf + r.offset) |=
encode_page(page(val) - page(get_addr(ctx) + r.offset));
break;
case ARM64_RELOC_PAGEOFF12:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: {
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
u32 insn = *(u32 *)(buf + r.offset);
i64 scale = 0;
if ((insn & 0x3b000000) == 0x39000000)
scale = insn >> 30;
*(u32 *)(buf + r.offset) |= bits(val, 11, scale) << 10;
break;
}
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
}
}
} // namespace mold::macho