mirror of
https://github.com/0O0o0oOoO00/Alas.git
synced 2026-05-20 04:39:29 +08:00
367 lines
10 KiB
C++
367 lines
10 KiB
C++
#include "platform_detect_macro.h"
|
|
|
|
#if defined(TARGET_ARCH_ARM64)
|
|
|
|
#include "InstructionRelocation/arm64/InstructionRelocationARM64.h"
|
|
|
|
#include "dobby/dobby_internal.h"
|
|
|
|
#include "core/arch/arm64/registers-arm64.h"
|
|
#include "core/assembler/assembler-arm64.h"
|
|
#include "core/codegen/codegen-arm64.h"
|
|
|
|
#include "inst_constants.h"
|
|
#include "inst_decode_encode_kit.h"
|
|
|
|
using namespace zz::arm64;
|
|
|
|
#if defined(DOBBY_DEBUG)
|
|
#define debug_nop() _ nop()
|
|
#else
|
|
#define debug_nop()
|
|
#endif
|
|
|
|
#define arm64_trunc_page(x) ((x) & (~(0x1000 - 1)))
|
|
#define arm64_round_page(x) trunc_page((x) + (0x1000 - 1))
|
|
|
|
typedef struct {
|
|
addr_t mapped_addr;
|
|
|
|
uint8_t *buffer;
|
|
uint8_t *buffer_cursor;
|
|
size_t buffer_size;
|
|
|
|
addr_t src_vmaddr;
|
|
addr_t dst_vmaddr;
|
|
|
|
CodeMemBlock *origin;
|
|
CodeMemBlock *relocated;
|
|
|
|
tinystl::unordered_map<off_t, off_t> relocated_offset_map;
|
|
|
|
tinystl::unordered_map<addr_t, AssemblerPseudoLabel *> label_map;
|
|
|
|
} relo_ctx_t;
|
|
|
|
// ---
|
|
|
|
addr_t relo_cur_src_vmaddr(relo_ctx_t *ctx) {
|
|
return ctx->src_vmaddr + (ctx->buffer_cursor - ctx->buffer);
|
|
}
|
|
|
|
addr_t relo_cur_dst_vmaddr(relo_ctx_t *ctx, TurboAssembler *assembler) {
|
|
return ctx->dst_vmaddr + assembler->GetCodeBuffer()->GetBufferSize();
|
|
}
|
|
|
|
addr_t relo_src_offset_to_vmaddr(relo_ctx_t *ctx, off_t offset) {
|
|
return ctx->src_vmaddr + offset;
|
|
}
|
|
|
|
addr_t relo_dst_offset_to_vmaddr(relo_ctx_t *ctx, off_t offset) {
|
|
return ctx->dst_vmaddr + offset;
|
|
}
|
|
|
|
// ---
|
|
|
|
#if 0
|
|
bool has_relo_label_at(relo_ctx_t *ctx, addr_t addr) {
|
|
if (ctx->label_map.count(addr)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AssemblerPseudoLabel *relo_label_create_or_get(relo_ctx_t *ctx, addr_t addr) {
|
|
if (!ctx->label_map.count(addr)) {
|
|
auto *label = new AssemblerPseudoLabel(addr);
|
|
ctx->label_map[addr] = label;
|
|
}
|
|
return ctx->label_map[addr];
|
|
}
|
|
|
|
int64_t relo_label_link_offset(relo_ctx_t *ctx, pcrel_type_t pcrel_type, int64_t offset) {
|
|
auto is_offset_undefined = [ctx](int64_t offset) -> bool {
|
|
if (ctx->buffer_cursor + offset < ctx->buffer || ctx->buffer_cursor + offset > ctx->buffer + ctx->buffer_size) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
auto is_offset_uninitialized = [ctx](int64_t offset) -> bool {
|
|
if (ctx->buffer_cursor + offset > ctx->buffer && ctx->buffer_cursor + offset < ctx->buffer + ctx->buffer_size) {
|
|
if (!ctx->relocated_offset_map.count(ctx->buffer_cursor + offset - ctx->buffer_cursor))
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
addr_t label_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
if (pcrel_type == RELO_ARM64_RELOC_PAGE21) {
|
|
label_vmaddr = arm64_trunc_page(label_vmaddr);
|
|
}
|
|
|
|
auto *label = relo_label_create_or_get(ctx, label_vmaddr);
|
|
if (is_offset_undefined(offset)) { // pc relative target is beyond our scope
|
|
label->link_to(AssemblerPseudoLabel::kLabelImm19, relo_cur_src_vmaddr(ctx), (addr_t)ctx->buffer_cursor - ctx->mapped_addr);
|
|
return 0;
|
|
} else if (is_offset_uninitialized(offset)) { // pc relative target is in our control, but not handle yet
|
|
label->link_to(AssemblerPseudoLabel::kLabelImm19, relo_cur_src_vmaddr(ctx), (addr_t)ctx->buffer_cursor - ctx->mapped_addr);
|
|
return 0;
|
|
} else { // pc relative target is already handled
|
|
off_t off = ctx->buffer_cursor + offset - ctx->buffer;
|
|
off_t relocated_off = label->pos();
|
|
int64_t new_offset = relo_dst_offset_to_vmaddr(ctx, relocated_off) - relo_src_offset_to_vmaddr(ctx, off);
|
|
return new_offset;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ---
|
|
|
|
static inline bool inst_is_b_bl(uint32_t instr) {
|
|
return (instr & UnconditionalBranchFixedMask) == UnconditionalBranchFixed;
|
|
}
|
|
|
|
static inline bool inst_is_ldr_literal(uint32_t instr) {
|
|
return ((instr & LoadRegLiteralFixedMask) == LoadRegLiteralFixed);
|
|
}
|
|
|
|
static inline bool inst_is_adr(uint32_t instr) {
|
|
return (instr & PCRelAddressingFixedMask) == PCRelAddressingFixed && (instr & PCRelAddressingMask) == ADR;
|
|
}
|
|
|
|
static inline bool inst_is_adrp(uint32_t instr) {
|
|
return (instr & PCRelAddressingFixedMask) == PCRelAddressingFixed && (instr & PCRelAddressingMask) == ADRP;
|
|
}
|
|
|
|
static inline bool inst_is_b_cond(uint32_t instr) {
|
|
return (instr & ConditionalBranchFixedMask) == ConditionalBranchFixed;
|
|
}
|
|
|
|
static inline bool inst_is_compare_b(uint32_t instr) {
|
|
return (instr & CompareBranchFixedMask) == CompareBranchFixed;
|
|
}
|
|
|
|
static inline bool inst_is_test_b(uint32_t instr) {
|
|
return (instr & TestBranchFixedMask) == TestBranchFixed;
|
|
}
|
|
|
|
// ---
|
|
|
|
int relo_relocate(relo_ctx_t *ctx, bool branch) {
|
|
int relocated_insn_count = 0;
|
|
|
|
TurboAssembler turbo_assembler_(0);
|
|
#define _ turbo_assembler_.
|
|
|
|
auto relocated_buffer = turbo_assembler_.GetCodeBuffer();
|
|
|
|
while (ctx->buffer_cursor < ctx->buffer + ctx->buffer_size) {
|
|
uint32_t orig_off = ctx->buffer_cursor - ctx->buffer;
|
|
uint32_t relocated_off = relocated_buffer->GetBufferSize();
|
|
ctx->relocated_offset_map[orig_off] = relocated_off;
|
|
|
|
#if 0
|
|
addr_t inst_vmaddr = 0;
|
|
inst_vmaddr = relo_cur_src_vmaddr(ctx);
|
|
if (has_relo_label_at(ctx, inst_vmaddr)) {
|
|
auto *label = relo_label_create_or_get(ctx, inst_vmaddr);
|
|
label->bind_to(inst_vmaddr);
|
|
}
|
|
#endif
|
|
|
|
arm64_inst_t inst = *(arm64_inst_t *)ctx->buffer_cursor;
|
|
if (inst_is_b_bl(inst)) {
|
|
DEBUG_LOG("%d:relo <b_bl> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_imm26_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
auto dst_label = RelocLabel::withData(dst_vmaddr);
|
|
_ AppendRelocLabel(dst_label);
|
|
|
|
{
|
|
_ Ldr(TMP_REG_0, dst_label);
|
|
if ((inst & UnconditionalBranchMask) == BL) {
|
|
_ blr(TMP_REG_0);
|
|
} else {
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
|
|
} else if (inst_is_ldr_literal(inst)) {
|
|
DEBUG_LOG("%d:relo <ldr_literal> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_imm19_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
int rt = decode_rt(inst);
|
|
char opc = bits(inst, 30, 31);
|
|
|
|
{
|
|
_ Mov(TMP_REG_0, dst_vmaddr);
|
|
if (opc == 0b00)
|
|
_ ldr(W(rt), MemOperand(TMP_REG_0, 0));
|
|
else if (opc == 0b01)
|
|
_ ldr(X(rt), MemOperand(TMP_REG_0, 0));
|
|
else {
|
|
UNIMPLEMENTED();
|
|
}
|
|
}
|
|
} else if (inst_is_adr(inst)) {
|
|
DEBUG_LOG("%d:relo <adr> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_immhi_immlo_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
int rd = decode_rd(inst);
|
|
|
|
{
|
|
_ Mov(X(rd), dst_vmaddr);
|
|
;
|
|
}
|
|
} else if (inst_is_adrp(inst)) {
|
|
DEBUG_LOG("%d:relo <adrp> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_immhi_immlo_zero12_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
dst_vmaddr = arm64_trunc_page(dst_vmaddr);
|
|
|
|
int rd = decode_rd(inst);
|
|
|
|
{
|
|
_ Mov(X(rd), dst_vmaddr);
|
|
;
|
|
}
|
|
} else if (inst_is_b_cond(inst)) {
|
|
DEBUG_LOG("%d:relo <b_cond> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_imm19_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
arm64_inst_t branch_instr = inst;
|
|
{
|
|
char cond = bits(inst, 0, 3);
|
|
cond = cond ^ 1;
|
|
set_bits(branch_instr, 0, 3, cond);
|
|
|
|
int64_t offset = 4 * 3;
|
|
uint32_t imm19 = offset >> 2;
|
|
set_bits(branch_instr, 5, 23, imm19);
|
|
}
|
|
|
|
auto dst_label = RelocLabel::withData(dst_vmaddr);
|
|
_ AppendRelocLabel(dst_label);
|
|
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, dst_label);
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
} else if (inst_is_compare_b(inst)) {
|
|
DEBUG_LOG("%d:relo <compare_b> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_imm19_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
arm64_inst_t branch_instr = inst;
|
|
{
|
|
char op = bit(inst, 24);
|
|
op = op ^ 1;
|
|
set_bit(branch_instr, 24, op);
|
|
|
|
int64_t offset = 4 * 3;
|
|
uint32_t imm19 = offset >> 2;
|
|
set_bits(branch_instr, 5, 23, imm19);
|
|
}
|
|
|
|
auto dst_label = RelocLabel::withData(dst_vmaddr);
|
|
_ AppendRelocLabel(dst_label);
|
|
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, dst_label);
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
} else if (inst_is_test_b(inst)) {
|
|
DEBUG_LOG("%d:relo <test_b> at %p", relocated_insn_count++, relo_cur_src_vmaddr(ctx));
|
|
|
|
int64_t offset = decode_imm14_offset(inst);
|
|
addr_t dst_vmaddr = relo_cur_src_vmaddr(ctx) + offset;
|
|
|
|
arm64_inst_t branch_instr = inst;
|
|
{
|
|
char op = bit(inst, 24);
|
|
op = op ^ 1;
|
|
set_bit(branch_instr, 24, op);
|
|
|
|
int64_t offset = 4 * 3;
|
|
uint32_t imm14 = offset >> 2;
|
|
set_bits(branch_instr, 5, 18, imm14);
|
|
}
|
|
|
|
auto dst_label = RelocLabel::withData(dst_vmaddr);
|
|
_ AppendRelocLabel(dst_label);
|
|
|
|
{
|
|
_ Emit(branch_instr);
|
|
{
|
|
_ Ldr(TMP_REG_0, dst_label);
|
|
_ br(TMP_REG_0);
|
|
}
|
|
}
|
|
} else {
|
|
_ Emit(inst);
|
|
}
|
|
|
|
ctx->buffer_cursor += sizeof(arm64_inst_t);
|
|
}
|
|
#undef _
|
|
|
|
// update origin
|
|
int new_origin_len = (addr_t)ctx->buffer_cursor - (addr_t)ctx->buffer;
|
|
ctx->origin->reset(ctx->origin->addr, new_origin_len);
|
|
|
|
// TODO: if last instr is unlink branch, ignore it
|
|
if (branch) {
|
|
CodeGen codegen(&turbo_assembler_);
|
|
codegen.LiteralLdrBranch(ctx->origin->addr + ctx->origin->size);
|
|
}
|
|
|
|
// Bind all labels
|
|
turbo_assembler_.RelocBind();
|
|
|
|
// Generate executable code
|
|
{
|
|
auto code = AssemblyCodeBuilder::FinalizeFromTurboAssembler(&turbo_assembler_);
|
|
ctx->relocated = code;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void GenRelocateCode(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated, bool branch) {
|
|
relo_ctx_t ctx = {0};
|
|
|
|
ctx.buffer = ctx.buffer_cursor = (uint8_t *)buffer;
|
|
ctx.buffer_size = origin->size;
|
|
|
|
ctx.src_vmaddr = (addr_t)origin->addr;
|
|
ctx.dst_vmaddr = (addr_t)relocated->addr;
|
|
|
|
ctx.origin = origin;
|
|
|
|
relo_relocate(&ctx, branch);
|
|
|
|
relocated->reset(ctx.relocated->addr, ctx.relocated->size);
|
|
}
|
|
|
|
void GenRelocateCodeAndBranch(void *buffer, CodeMemBlock *origin, CodeMemBlock *relocated) {
|
|
GenRelocateCode(buffer, origin, relocated, true);
|
|
}
|
|
|
|
#endif
|