1
0
mirror of https://github.com/0O0o0oOoO00/Alas.git synced 2026-05-20 04:39:29 +08:00
Files
Alas/blcrack/cracker/Dobby/source/InstructionRelocation/arm64/InstructionRelocationARM64.cc
2025-11-01 00:23:46 +08:00

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