Skip to content

Use it bro #2

@QuinnEddie

Description

@QuinnEddie

// ============================================================
// ARM64 指令模拟器
//
// 覆盖范围:
// 1. 跳转/调用类:b / bl / br / blr / ret / b.cond / cbz / cbnz / tbz / tbnz
// 2. Load/Store 类:
// - LDR/STR 立即数偏移 / 寄存器偏移 / unscaled / pre / post
// - LDP/STP 寄存器对
// - 支持通用寄存器 (GPR) 和 浮点寄存器 (FP/SIMD: B,H,S,D,Q)
// 3. 基础整数算术类 (ALU):
// - ADD, SUB, ADDS, SUBS (CMP) 的立即数与寄存器版本
// 4. 基础浮点算术类 (FPU硬件辅助计算):
// - FADD, FSUB, FMUL, FDIV (单精度 S / 双精度 D)
// ============================================================

#ifndef EMULATE_INSN_H
#define EMULATE_INSN_H

#include <linux/uaccess.h>
#include <asm/ptrace.h>
#include <asm/insn.h>

// ---- 通用寄存器辅助宏 ---------------------------------------
#define REG_READ(regs, n) ((n) == 31 ? 0ULL : (regs)->regs[(n)])

#define REG_WRITE(regs, n, val, sf)
do
{
if ((n) != 31)
(regs)->regs[(n)] = (sf) ? (u64)(val)
: (u64)(u32)(val);
} while (0)

#define ADDR_REG(regs, n) ((n) == 31 ? (regs)->sp : (regs)->regs[(n)])

#define ADDR_REG_WRITE(regs, n, val)
do
{
if ((n) == 31)
(regs)->sp = (u64)(val);
else
(regs)->regs[(n)] = (u64)(val);
} while (0)

// ---- FP/SIMD 寄存器辅助宏 -----------------------------------
#define VREG_READ_LO(vregs, n) ((vregs)[(n) * 2])
#define VREG_READ_HI(vregs, n) ((vregs)[(n) * 2 + 1])

#define VREG_WRITE_SCALAR(vregs, n, val)
do {
(vregs)[(n) * 2] = (u64)(val);
(vregs)[(n) * 2 + 1] = 0;
} while (0)

#define VREG_WRITE_Q(vregs, n, val_lo, val_hi)
do {
(vregs)[(n) * 2] = (val_lo);
(vregs)[(n) * 2 + 1] = (val_hi);
} while (0)

// ---- 状态寄存器辅助宏 ---------------------------------------
#define PSTATE_N(pstate) (((pstate) >> 31) & 1)
#define PSTATE_Z(pstate) (((pstate) >> 30) & 1)
#define PSTATE_C(pstate) (((pstate) >> 29) & 1)
#define PSTATE_V(pstate) (((pstate) >> 28) & 1)

static bool eval_cond(u64 pstate, u32 cond)
{
bool n = PSTATE_N(pstate), z = PSTATE_Z(pstate);
bool c = PSTATE_C(pstate), v = PSTATE_V(pstate), result;
switch (cond >> 1)
{
case 0: result = z; break;
case 1: result = c; break;
case 2: result = n; break;
case 3: result = v; break;
case 4: result = c && !z; break;
case 5: result = (n == v); break;
case 6: result = (n == v) && !z; break;
case 7: result = true; break;
default: result = false; break;
}
if ((cond & 1) && (cond != 0xf))
result = !result;
return result;
}

// ---- 用户态内存访问包装 -------------------------------------
static inline int user_read_u8(u64 addr, u8 *val) { return get_user(*val, (u8 __user *)(uintptr_t)addr); }
static inline int user_read_u16(u64 addr, u16 *val) { return get_user(*val, (u16 __user *)(uintptr_t)addr); }
static inline int user_read_u32(u64 addr, u32 *val) { return get_user(*val, (u32 __user *)(uintptr_t)addr); }
static inline int user_read_u64(u64 addr, u64 *val) { return get_user(*val, (u64 __user *)(uintptr_t)addr); }
static inline int user_write_u8(u64 addr, u8 val) { return put_user(val, (u8 __user *)(uintptr_t)addr); }
static inline int user_write_u16(u64 addr, u16 val) { return put_user(val, (u16 __user *)(uintptr_t)addr); }
static inline int user_write_u32(u64 addr, u32 val) { return put_user(val, (u32 __user *)(uintptr_t)addr); }
static inline int user_write_u64(u64 addr, u64 val) { return put_user(val, (u64 __user *)(uintptr_t)addr); }
static inline int read_user_insn(u64 pc, u32 *insn) { return get_user(*insn, (u32 __user *)(uintptr_t)pc); }

// ============================================================
// 模拟执行 ARM64 指令
// @regs: 普通寄存器上下文
// @vregs: FP/SIMD 寄存器数组 (长度至少 64 的 u64 数组)
// ============================================================
static bool emulate_insn(struct pt_regs *regs, u64 *vregs)
{
u32 insn;
u64 pc = regs->pc;

if (read_user_insn(pc, &insn) != 0)
{
    regs->pc += 4;
    return false;
}

// --- 第一部分:跳转 / 调用指令 ---
if ((insn & 0xFC000000) == 0x14000000) { regs->pc = pc + sign_extend64((s64)(insn & 0x3FFFFFF) << 2, 27); return true; }
if ((insn & 0xFC000000) == 0x94000000) { regs->regs[30] = pc + 4; regs->pc = pc + sign_extend64((s64)(insn & 0x3FFFFFF) << 2, 27); return true; }
if ((insn & 0xFE1F03E0) == 0xD61F0000) {
    u32 rn = (insn >> 5) & 0x1F, opc = (insn >> 21) & 0x3;
    if (opc == 1) regs->regs[30] = pc + 4;
    if (opc <= 2) { regs->pc = regs->regs[rn]; return true; }
}
if ((insn & 0xFF000010) == 0x54000000) {
    s64 offset = sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20);
    regs->pc = eval_cond(regs->pstate, insn & 0xF) ? (pc + offset) : (pc + 4);
    return true;
}
if ((insn & 0x7E000000) == 0x34000000) {
    u32 rt = insn & 0x1F; u64 val = ((insn >> 31) & 1) ? REG_READ(regs, rt) : (u32)REG_READ(regs, rt);
    bool jump = ((insn >> 24) & 1) ? (val != 0) : (val == 0);
    regs->pc = jump ? (pc + sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20)) : (pc + 4);
    return true;
}
if ((insn & 0x7E000000) == 0x36000000) {
    u32 rt = insn & 0x1F, bit_pos = ((insn >> 31) & 1) << 5 | ((insn >> 19) & 0x1F);
    bool bit_set = (REG_READ(regs, rt) >> bit_pos) & 1;
    bool jump = ((insn >> 24) & 1) ? bit_set : !bit_set;
    regs->pc = jump ? (pc + sign_extend64((s64)((insn >> 5) & 0x3FFF) << 2, 15)) : (pc + 4);
    return true;
}

// --- 第二部分:Load/Store 指令 ---
if ((insn & 0x0A000000) == 0x08000000)
{
    u32 size = (insn >> 30) & 0x3;
    bool is_simd = ((insn >> 26) & 1) != 0; 

    // LDR literal (PC relative)
    if (!is_simd && (insn & 0x3B000000) == 0x18000000) {
        u32 rt = insn & 0x1F;
        u64 addr = pc + sign_extend64((s64)((insn >> 5) & 0x7FFFF) << 2, 20);
        if (((insn >> 30) & 0x3) == 2) { u32 t; if (user_read_u32(addr, &t)) goto fault; REG_WRITE(regs, rt, (s64)(s32)t, 1); }
        else if ((insn >> 30) & 1) { u64 t; if (user_read_u64(addr, &t)) goto fault; REG_WRITE(regs, rt, t, 1); }
        else { u32 t; if (user_read_u32(addr, &t)) goto fault; REG_WRITE(regs, rt, t, 0); }
        regs->pc += 4; return true;
    }

    // LDP / STP
    if ((insn & 0x3A000000) == (is_simd ? 0x2C000000 : 0x28000000)) {
        u32 opc_pair = (insn >> 30) & 0x3, l = (insn >> 22) & 1, idx = (insn >> 23) & 0x3;
        u32 rn = (insn >> 5) & 0x1F, rt = insn & 0x1F, rt2 = (insn >> 10) & 0x1F;
        int pair_bytes = 0;
        if (is_simd) {
            if (opc_pair == 0) pair_bytes = 4; else if (opc_pair == 1) pair_bytes = 8; else if (opc_pair == 2) pair_bytes = 16; else goto fault;
        } else {
            pair_bytes = (opc_pair == 2) ? 8 : 4;
        }
        s64 offset = sign_extend64((s64)((insn >> 15) & 0x7F), 6) * pair_bytes;
        u64 base = ADDR_REG(regs, rn), addr;
        if (idx == 1) addr = base; else if (idx == 2 || idx == 3) addr = base + offset; else goto fault;

        if (l) { // Load
            if (is_simd) {
                if (pair_bytes == 4) { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; VREG_WRITE_SCALAR(vregs, rt, v1); VREG_WRITE_SCALAR(vregs, rt2, v2); }
                else if (pair_bytes == 8) { u64 v1, v2; if (user_read_u64(addr, &v1) || user_read_u64(addr+8, &v2)) goto fault; VREG_WRITE_SCALAR(vregs, rt, v1); VREG_WRITE_SCALAR(vregs, rt2, v2); }
                else { u64 tl, th, tl2, th2; if (user_read_u64(addr, &tl) || user_read_u64(addr+8, &th) || user_read_u64(addr+16, &tl2) || user_read_u64(addr+24, &th2)) goto fault; VREG_WRITE_Q(vregs, rt, tl, th); VREG_WRITE_Q(vregs, rt2, tl2, th2); }
            } else {
                if (opc_pair == 2) { u64 v1, v2; if (user_read_u64(addr, &v1) || user_read_u64(addr+8, &v2)) goto fault; REG_WRITE(regs, rt, v1, 1); REG_WRITE(regs, rt2, v2, 1); }
                else if (opc_pair == 1) { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; REG_WRITE(regs, rt, (s64)(s32)v1, 1); REG_WRITE(regs, rt2, (s64)(s32)v2, 1); }
                else { u32 v1, v2; if (user_read_u32(addr, &v1) || user_read_u32(addr+4, &v2)) goto fault; REG_WRITE(regs, rt, v1, 0); REG_WRITE(regs, rt2, v2, 0); }
            }
        } else { // Store
            if (is_simd) {
                if (pair_bytes == 4) { if (user_write_u32(addr, (u32)VREG_READ_LO(vregs, rt)) || user_write_u32(addr+4, (u32)VREG_READ_LO(vregs, rt2))) goto fault; }
                else if (pair_bytes == 8) { if (user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_LO(vregs, rt2))) goto fault; }
                else { if (user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_HI(vregs, rt)) || user_write_u64(addr+16, VREG_READ_LO(vregs, rt2)) || user_write_u64(addr+24, VREG_READ_HI(vregs, rt2))) goto fault; }
            } else {
                if (opc_pair == 2) { if (user_write_u64(addr, REG_READ(regs, rt)) || user_write_u64(addr+8, REG_READ(regs, rt2))) goto fault; }
                else { if (user_write_u32(addr, (u32)REG_READ(regs, rt)) || user_write_u32(addr+4, (u32)REG_READ(regs, rt2))) goto fault; }
            }
        }
        if (idx == 1 || idx == 3) ADDR_REG_WRITE(regs, rn, base + offset);
        regs->pc += 4; return true;
    }

    // LDR / STR (Single)
    if ((insn & 0x3E000000) == 0x38000000 || (insn & 0x3E000000) == 0x3A000000 ||
        (insn & 0x3E000000) == 0x3C000000 || (insn & 0x3E000000) == 0x3E000000 ||
        (insn & 0x3F000000) == 0x39000000 || (insn & 0x3F000000) == 0x79000000 ||
        (insn & 0x3F000000) == 0xB9000000 || (insn & 0x3F000000) == 0xF9000000 ||
        (is_simd && ((insn & 0x3B000000) == 0x39000000 || (insn & 0x3B000000) == 0x3D000000))) 
    {
        u32 rn = (insn >> 5) & 0x1F, rt = insn & 0x1F, opc = (insn >> 22) & 0x3;
        bool is_load, sign_ext = false, dest_64 = false; int access_bytes;
        if (is_simd) {
            is_load = (opc & 1) != 0; 
            if (((insn >> 23) & 1) == 1) access_bytes = 16; else access_bytes = (1 << size);
        } else {
            is_load = (opc != 0); sign_ext = (opc == 2) || (opc == 3);
            dest_64 = (size == 3) || (opc == 2); access_bytes = (1 << size);
        }

        u64 base = ADDR_REG(regs, rn), addr;
        if (((insn >> 24) & 1) == 1) { addr = base + ((insn >> 10) & 0xFFF) * (u64)access_bytes; }
        else {
            u32 idx = (insn >> 10) & 0x3;
            if (idx == 0) { addr = base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8); }
            else if (idx == 1) { addr = base; ADDR_REG_WRITE(regs, rn, base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8)); }
            else if (idx == 3) { addr = base + sign_extend64((s64)((insn >> 12) & 0x1FF), 8); ADDR_REG_WRITE(regs, rn, addr); }
            else if (idx == 2) {
                if (((insn >> 21) & 1) == 1) {
                    u32 rm = (insn >> 16) & 0x1F, option = (insn >> 13) & 0x7; s64 ext_val; u64 rm_val = REG_READ(regs, rm);
                    switch (option) { case 2: ext_val=(s64)(u32)rm_val; break; case 3: ext_val=(s64)rm_val; break; case 6: ext_val=(s64)(s32)rm_val; break; case 7: ext_val=(s64)rm_val; break; default: ext_val=(s64)rm_val; break; }
                    if ((insn >> 12) & 1) ext_val <<= size;
                    addr = base + (u64)ext_val;
                } else goto fault;
            } else goto fault;
        }

        if (is_load) {
            if (is_simd) {
                u64 vl = 0;
                if (access_bytes <= 8) {
                    switch(access_bytes) { case 1:{u8 t;if(user_read_u8(addr,&t))goto fault;vl=t;break;} case 2:{u16 t;if(user_read_u16(addr,&t))goto fault;vl=t;break;} case 4:{u32 t;if(user_read_u32(addr,&t))goto fault;vl=t;break;} case 8:{u64 t;if(user_read_u64(addr,&t))goto fault;vl=t;break;} }
                    VREG_WRITE_SCALAR(vregs, rt, vl);
                } else { u64 tl, th; if(user_read_u64(addr, &tl) || user_read_u64(addr+8, &th))goto fault; VREG_WRITE_Q(vregs, rt, tl, th); }
            } else {
                u64 val = 0;
                switch(access_bytes) { case 1:{u8 t;if(user_read_u8(addr,&t))goto fault;val=t;break;} case 2:{u16 t;if(user_read_u16(addr,&t))goto fault;val=t;break;} case 4:{u32 t;if(user_read_u32(addr,&t))goto fault;val=t;break;} case 8:{u64 t;if(user_read_u64(addr,&t))goto fault;val=t;break;} }
                if (sign_ext) { int bits = access_bytes * 8 - 1; if (val & (1ULL << bits)) val |= ~((1ULL << (bits + 1)) - 1); }
                REG_WRITE(regs, rt, val, dest_64 ? 1 : 0);
            }
        } else {
            if (is_simd) {
                if (access_bytes <= 8) {
                    u64 v = VREG_READ_LO(vregs, rt);
                    switch(access_bytes) { case 1:if(user_write_u8(addr,(u8)v))goto fault;break; case 2:if(user_write_u16(addr,(u16)v))goto fault;break; case 4:if(user_write_u32(addr,(u32)v))goto fault;break; case 8:if(user_write_u64(addr,v))goto fault;break; }
                } else { if(user_write_u64(addr, VREG_READ_LO(vregs, rt)) || user_write_u64(addr+8, VREG_READ_HI(vregs, rt))) goto fault; }
            } else {
                u64 v = REG_READ(regs, rt);
                switch(access_bytes) { case 1:if(user_write_u8(addr,(u8)v))goto fault;break; case 2:if(user_write_u16(addr,(u16)v))goto fault;break; case 4:if(user_write_u32(addr,(u32)v))goto fault;break; case 8:if(user_write_u64(addr,v))goto fault;break; }
            }
        }
        regs->pc += 4; return true;
    }
}

// --- 第三部分:基础算术指令 (整数 ALU) ---
// ADD/SUB (immediate)
if ((insn & 0x1F000000) == 0x11000000) {
    bool sf = (insn >> 31) & 1, op = (insn >> 30) & 1, S = (insn >> 29) & 1;
    u32 shift = (insn >> 22) & 0x3, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
    u64 imm = (insn >> 10) & 0xFFF;
    if (shift == 1) imm <<= 12; else if (shift > 1) goto unsupported_insn;
    u64 op1 = (rn == 31) ? regs->sp : REG_READ(regs, rn);
    if (!sf) { op1 = (u32)op1; imm = (u32)imm; }
    u64 result = op ? (op1 - imm) : (op1 + imm);
    if (S) {
        u32 n, z, c, v;
        if (!sf) { u32 r32=(u32)result; n=(r32>>31)&1; z=(r32==0); if(!op){c=(r32<(u32)op1);v=(((u32)op1^~((u32)imm))&((u32)op1^r32))>>31;}else{c=((u32)op1>=(u32)imm);v=(((u32)op1^(u32)imm)&((u32)op1^r32))>>31;} }
        else { n=(result>>63)&1; z=(result==0); if(!op){c=(result<op1);v=((op1^~imm)&(op1^result))>>63;}else{c=(op1>=imm);v=((op1^imm)&(op1^result))>>63;} }
        regs->pstate = (regs->pstate & ~0xF0000000ULL) | ((u64)n<<31)|((u64)z<<30)|((u64)c<<29)|((u64)v<<28);
    }
    if (rd != 31 || !S) { if (rd == 31) regs->sp = result; else REG_WRITE(regs, rd, result, sf); }
    regs->pc += 4; return true;
}
// ADD/SUB (shifted register)
if ((insn & 0x1F200000) == 0x0B000000) {
    bool sf = (insn >> 31) & 1, op = (insn >> 30) & 1, S = (insn >> 29) & 1;
    u32 shift = (insn >> 22) & 0x3, rm = (insn >> 16) & 0x1F, imm6 = (insn >> 10) & 0x3F, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
    u64 op1 = REG_READ(regs, rn), op2 = REG_READ(regs, rm);
    if (!sf) { op1 = (u32)op1; op2 = (u32)op2; }
    if (shift == 0) op2 <<= imm6; else if (shift == 1) op2 = sf ? (op2 >> imm6) : ((u32)op2 >> imm6); else if (shift == 2) op2 = sf ? ((s64)op2 >> imm6) : (u64)((s32)op2 >> imm6); else goto unsupported_insn;
    if (!sf) op2 = (u32)op2;
    u64 result = op ? (op1 - op2) : (op1 + op2);
    if (rd != 31 || !S) REG_WRITE(regs, rd, result, sf);
    regs->pc += 4; return true;
}

// --- 第四部分:浮点/SIMD 算术指令 (利用硬件计算) ---
// FADD, FSUB, FMUL, FDIV (Scalar)
if ((insn & 0x5F200800) == 0x1E200800) {
    u32 type = (insn >> 22) & 0x3;
    u32 opcode = (insn >> 12) & 0xF;
    u32 rm = (insn >> 16) & 0x1F, rn = (insn >> 5) & 0x1F, rd = insn & 0x1F;
    if (type > 1) goto unsupported_insn; // Only S and D

    u64 op1_val = VREG_READ_LO(vregs, rn), op2_val = VREG_READ_LO(vregs, rm), res_val = 0;

    if (type == 0) { // Single precision (32-bit)
        u32 op1_s = (u32)op1_val, op2_s = (u32)op2_val, res_s = 0;
        switch (opcode) {
            case 0x0: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fmul s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
            case 0x1: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fdiv s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
            case 0x2: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fadd s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
            case 0x3: asm volatile(".arch_extension fp\n fmov s1, %w1\n fmov s2, %w2\n fsub s0, s1, s2\n fmov %w0, s0\n" : "=r"(res_s) : "r"(op1_s), "r"(op2_s) : "s0", "s1", "s2"); break;
            default: goto unsupported_insn;
        }
        res_val = res_s;
    } else { // Double precision (64-bit)
        switch (opcode) {
            case 0x0: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fmul d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
            case 0x1: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fdiv d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
            case 0x2: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fadd d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
            case 0x3: asm volatile(".arch_extension fp\n fmov d1, %1\n fmov d2, %2\n fsub d0, d1, d2\n fmov %0, d0\n" : "=r"(res_val) : "r"(op1_val), "r"(op2_val) : "d0", "d1", "d2"); break;
            default: goto unsupported_insn;
        }
    }
    VREG_WRITE_SCALAR(vregs, rd, res_val);
    regs->pc += 4; return true;
}

unsupported_insn:
// 其他未支持的指令退化为 PC+4 跳过
pr_debug("hwbp: 不支持模拟 insn=0x%08x pc=0x%llx,退化为 pc+4\n", insn, pc);
regs->pc += 4;
return false;

fault:
pr_debug("hwbp: 模拟访存失败 insn=0x%08x pc=0x%llx\n", insn, pc);
return false;
}

#endif // EMULATE_INSN_H

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions