/*
 * arch_port.c - CPU 架构移植层实现
 *
 * 对应 arch_port.h：寄存器抽象访问、ptrace 寄存器读写、断点指令字节、
 * NOP 填充判断。每个架构分支均标注 ABI 依据；新增架构时按相同模式
 * 补充分支即可。
 *
 * 注意：arch_regs_t 的布局随架构不同，本文件内部分支全部
 * 依赖 <sys/user.h> 的架构专用定义，业务代码不得直接访问这些字段。
 */

#include "lnxall_trace.h"
#include "arch_port.h"

/* 架构断点/NOP 指令字节（文件级常量，避免函数内 static 局部变量） */
#ifdef ARCH_AARCH64
static const unsigned char g_arch_break_insn[4] = { 0x00, 0x00, 0x20, 0xD4 }; /* BRK #0 */
static const unsigned char g_arch_nop_insn[4]   = { 0x1F, 0x20, 0x03, 0xD5 }; /* NOP  */
#elif defined(ARCH_ARM32)
static const unsigned char g_arch_break_insn[2] = { 0x00, 0xBE }; /* Thumb BKPT #0 */
static const unsigned char g_arch_nop_insn[2]   = { 0x00, 0xBF }; /* Thumb NOP     */
#else
static const unsigned char g_arch_break_insn[1] = { 0xCC };       /* INT3 */
#endif

/* ========================================================================
 * 寄存器快照访问
 * ======================================================================== */
#ifdef ARCH_AARCH64
/* aarch64 <sys/user.h>：regs[31] + sp + pc + pstate */

unsigned long long arch_reg_pc(const arch_regs_t *r)
{ return r->pc; }

void arch_reg_set_pc(arch_regs_t *r, unsigned long long v)
{ r->pc = v; }

unsigned long long arch_reg_sp(const arch_regs_t *r)
{ return r->sp; }

void arch_reg_set_sp(arch_regs_t *r, unsigned long long v)
{ r->sp = v; }

unsigned long long arch_reg_arg(const arch_regs_t *r, int i)
{ return r->regs[i]; }

void arch_reg_set_arg(arch_regs_t *r, int i, unsigned long long v)
{ r->regs[i] = v; }

unsigned long long arch_reg_ret(const arch_regs_t *r)
{ return r->regs[0]; }

void arch_reg_set_lr(arch_regs_t *r, unsigned long long v)
{ r->regs[30] = v; }   /* x30 = LR */

unsigned long long arch_ret_addr(unsigned long long addr)
{ return addr; }       /* AArch64 返回地址无 Thumb bit */
#endif /* ARCH_AARCH64 */

#ifdef ARCH_ARM32
/* arm32 <sys/user.h>：uregs[18] = r0..r12, sp(13), lr(14), pc(15),
 * cpsr(16), orig_r0(17)
 *
 * Thumb-2 模式：写 PC 时置位低 bit（ARCH_PC_THUMB_BIT）进入 Thumb 状态；
 * 内核写回时据此清除 PC 低 bit 并同步 CPSR T 位，读回 PC 为纯地址。 */

unsigned long long arch_reg_pc(const arch_regs_t *r)
{ return r->uregs[15]; }

void arch_reg_set_pc(arch_regs_t *r, unsigned long long v)
{
    /* 入口指令集自适应（call 注入）：不再假定目标函数都是 Thumb-2。
     * 1) ELF 函数符号地址 bit0 是权威 Thumb 标记（GNU 工具链对 Thumb
     *    函数 st_value 置 bit0，symbol_parse.c 原样保留）——置位则按
     *    Thumb 进入，否则按 ARM 进入；
     * 2) bit0 缺失（个别工具链/符号清位）时退回继承现场 CPSR.T：被打断
     *    线程正处在哪种指令集，就以哪种状态进入被调函数。
     * 内核 SETREGS 后按 CPSR.T 恢复执行，故须同时写 PC（清 bit0 的
     *    纯地址）与 CPSR.T，二者缺一会解码错乱（SIGSEGV/UNDEF）。 */
    unsigned long cpsr = r->uregs[16];
    int thumb;
    if ((unsigned long)v & ARCH_PC_THUMB_BIT)
        thumb = 1;
    else
        thumb = (cpsr & ARCH_CPSR_T_BIT) ? 1 : 0;
    r->uregs[15] = (unsigned long)v & ~(unsigned long)ARCH_PC_THUMB_BIT;
    if (thumb)
        r->uregs[16] = cpsr | (unsigned long)ARCH_CPSR_T_BIT;
    else
        r->uregs[16] = cpsr & ~(unsigned long)ARCH_CPSR_T_BIT;
}

unsigned long long arch_reg_sp(const arch_regs_t *r)
{ return r->uregs[13]; }

void arch_reg_set_sp(arch_regs_t *r, unsigned long long v)
{ r->uregs[13] = (unsigned long)v; }

unsigned long long arch_reg_arg(const arch_regs_t *r, int i)
{ return r->uregs[i]; }

void arch_reg_set_arg(arch_regs_t *r, int i, unsigned long long v)
{ r->uregs[i] = (unsigned long)v; }

unsigned long long arch_reg_ret(const arch_regs_t *r)
{ return r->uregs[0]; }

void arch_reg_set_lr(arch_regs_t *r, unsigned long long v)
{ r->uregs[14] = (unsigned long)v; }   /* r14 = LR */

unsigned long long arch_ret_addr(unsigned long long addr)
{
    /* 返回断点槽位恒以 Thumb 状态命中：LR 恒置 bit0(=slot|1)。无论
     * callee 是 Thumb 还是 ARM 指令集，AAPCS 编译器生成的返回指令
     * （bx lr / pop {pc}）都会按 LR bit0 做 interworking 切换——ARM 态
     * callee 以 bx lr 返回时同样会因 bit0=1 切回 Thumb，命中我们写在
     * Thumb NOP 槽里的 BKPT（0x196a76 而非 0x196a77）。
     * 例外：非 interworking 的手写返回（mov pc, lr）无法切换状态，
     * 此类函数不支持 call 注入。 */
    return addr | (unsigned long long)ARCH_PC_THUMB_BIT;
}
#endif /* ARCH_ARM32 */

#ifdef ARCH_X86_64
/* x86_64 <sys/user.h>：整型参数 rdi,rsi,rdx,rcx,r8,r9；返回值 rax；
 * SP=rsp；PC=rip。x86 无 LR，arch_reg_set_lr 为空操作（返回地址压栈）。 */

unsigned long long arch_reg_pc(const arch_regs_t *r)
{ return r->rip; }

void arch_reg_set_pc(arch_regs_t *r, unsigned long long v)
{ r->rip = v; }

unsigned long long arch_reg_sp(const arch_regs_t *r)
{ return r->rsp; }

void arch_reg_set_sp(arch_regs_t *r, unsigned long long v)
{ r->rsp = v; }

unsigned long long arch_reg_arg(const arch_regs_t *r, int i)
{
    switch (i) {
    case 0: return r->rdi;
    case 1: return r->rsi;
    case 2: return r->rdx;
    case 3: return r->rcx;
    case 4: return r->r8;
    case 5: return r->r9;
    default: return 0;
    }
}

void arch_reg_set_arg(arch_regs_t *r, int i, unsigned long long v)
{
    switch (i) {
    case 0: r->rdi = v; break;
    case 1: r->rsi = v; break;
    case 2: r->rdx = v; break;
    case 3: r->rcx = v; break;
    case 4: r->r8  = v; break;
    case 5: r->r9  = v; break;
    default: break;
    }
}

unsigned long long arch_reg_ret(const arch_regs_t *r)
{ return r->rax; }

void arch_reg_set_lr(arch_regs_t *r, unsigned long long v)
{ (void)r; (void)v; }   /* x86 无 LR：注入机制需 push 返回地址（TODO） */

unsigned long long arch_ret_addr(unsigned long long addr)
{ return addr; }       /* x86 无 Thumb bit */
#endif /* ARCH_X86_64 */

/* ========================================================================
 * ptrace 寄存器读写
 *
 * 各架构统一走 PTRACE_GETREGSET / PTRACE_SETREGSET + NT_PRSTATUS：
 * aarch64 无传统 PTRACE_GETREGS；arm32/x86 的 GETREGSET 填充的寄存器
 * 布局与 <sys/user.h> 的 arch_regs_t 一致，iov 长度按
 * sizeof 自动适配，因此实现本身与架构无关。
 * ======================================================================== */

/**
 * @brief 读取目标进程全部寄存器快照
 *
 * @param[in]  pid   目标进程
 * @param[out] regs  寄存器快照缓冲区
 * @return 0 成功，-1 失败
 */
int arch_get_regs(pid_t pid, arch_regs_t *regs)
{
    struct iovec iov;
    iov.iov_base = regs;
    iov.iov_len  = sizeof(*regs);
    if (ptrace(PTRACE_GETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) {
        perror("PTRACE_GETREGSET");
        return -1;
    }
    return 0;
}

/**
 * @brief 写回目标进程全部寄存器快照
 *
 * @param[in] pid   目标进程
 * @param[in] regs  寄存器快照缓冲区
 * @return 0 成功，-1 失败
 */
int arch_set_regs(pid_t pid, const arch_regs_t *regs)
{
    struct iovec iov;
    iov.iov_base = (void *)regs;
    iov.iov_len  = sizeof(*regs);
    if (ptrace(PTRACE_SETREGSET, pid, (void *)NT_PRSTATUS, &iov) == -1) {
        perror("PTRACE_SETREGSET");
        return -1;
    }
    return 0;
}

/* ========================================================================
 * 断点指令（注入调用的返回断点 trap）
 *
 * aarch64: BRK #0   = 0xD4200000（LE 00 00 20 D4），停在本指令
 * arm32:   BKPT #0 = 0xBE00（Thumb 16 位，LE 00 BE），PC 修正到本指令
 * x86:     INT3    = 0xCC（单字节），RIP 指向断点之后 1 字节
 * ======================================================================== */

/**
 * @brief 返回断点指令字节流
 *
 * @return 指向断点指令字节数组的指针，长度由 arch_break_insn_len 给出
 */
const unsigned char *arch_break_insn(void)
{
    return g_arch_break_insn;
}

/**
 * @brief 返回断点指令长度
 *
 * @return 字节数（aarch64=4，arm32=2，x86=1）
 */
int arch_break_insn_len(void)
{
    return ARCH_BP_INSN_LEN;
}

/* ========================================================================
 * NOP 填充槽位判断
 * ======================================================================== */

/**
 * @brief 判断缓冲区 off 偏移处是否为 NOP 指令
 *
 * 供 call 命令扫描 .text 尾部 NOP 填充区使用；指令宽度按架构取
 * ARCH_NOP_STEP（aarch64=4 字节，arm32 Thumb=2 字节）。
 *
 * @param[in] buf 已读回的目标代码缓冲区
 * @param[in] off 偏移（需按 ARCH_NOP_STEP 对齐）
 * @return 1 是 NOP，0 不是
 */
int arch_buf_is_nop(const unsigned char *buf, size_t off)
{
#ifdef ARCH_AARCH64
    return memcmp(buf + off, g_arch_nop_insn, 4) == 0;
#elif defined(ARCH_ARM32)
    return memcmp(buf + off, g_arch_nop_insn, 2) == 0;
#else
    (void)buf; (void)off; return 0;   /* x86：NOP 变长，槽位扫描未实现 */
#endif
}
