/*
 * arch_port.h - CPU 架构移植层（隔离所有架构强相关代码）
 *
 * 当前完整支持：AArch64（ARM64）
 * 已预留实现：ARM32（ARMv7，Thumb-2 模式）/ x86_64（调用机制细节见各处 TODO）
 *
 * 本文件与 arch_port.c 集中承载：
 *   1. 架构检测与调用约定特征（参数寄存器数、栈对齐、断点 PC 语义）
 *   2. 寄存器快照访问抽象（PC/SP/参数寄存器/返回值/LR）
 *   3. ptrace 寄存器读写封装（GETREGSET / SETREGSET）
 *   4. 断点指令字节（BRK / Thumb BKPT / INT3）与 NOP 填充判断
 *
 * 业务代码一律通过本层接口访问寄存器与指令常量，
 * 禁止直接引用 arch_regs_t 的架构相关字段。
 */
#ifndef ARCH_PORT_H
#define ARCH_PORT_H

#include <sys/user.h>

/* ========================================================================
 * 1. 架构检测
 * ======================================================================== */
#if defined(__aarch64__)
  #define ARCH_AARCH64 1
#elif defined(__arm__)
  #define ARCH_ARM32   1
#elif defined(__x86_64__)
  #define ARCH_X86_64  1
#else
  #error "arch_port: unsupported CPU (need aarch64 / arm / x86_64)"
#endif

/* ========================================================================
 * 2. 调用约定特征
 * ======================================================================== */
#ifdef ARCH_AARCH64
  #define ARCH_MAX_INT_ARGS 8     /* AAPCS64：整型参数 x0..x7 */
  #define ARCH_MAX_CALL_ARGS 8    /* 单次 call 参数上限 = 寄存器数（x0..x7
                                    之外 AAPCS64 栈传参未实现，超限报错） */
  #define ARCH_STACK_ALIGN  16    /* SP 在函数入口需 16 字节对齐 */
  #define ARCH_LR_REGNO     30    /* x30 = LR */
  #define ARCH_BP_PC_DELTA  0     /* BRK 停下时 PC 落在断点指令本身 */
  #define ARCH_BP_INSN_LEN  4     /* BRK #0 长度 */
  #define ARCH_NOP_STEP     4     /* NOP（32 位）步长 */
#elif defined(ARCH_ARM32)
  /* ARMv7 Thumb-2 模式：BKPT #0（16 位 0xBE00）触发断点，NOP（16 位
   * 0xBF00）作填充；写 PC 时置位低 bit 以进入 Thumb 状态。 */
  #define ARCH_MAX_INT_ARGS 4     /* AAPCS32：整型参数 r0..r3 */
  #define ARCH_MAX_CALL_ARGS 8    /* 参数上限：r0..r3 + 第 5..8 参压入
                                     AAPCS32 栈参数区（入口 SP 起、4B/参） */
  #define ARCH_STACK_ALIGN  8     /* AAPCS32：SP 8 字节对齐 */
  #define ARCH_LR_REGNO     14    /* r14 = LR */
  #define ARCH_BP_PC_DELTA  0     /* Thumb BKPT：PC 修正到断点指令本身 */
  #define ARCH_BP_INSN_LEN  2     /* Thumb BKPT #0（0xBE00）长度 */
  #define ARCH_NOP_STEP     2     /* Thumb NOP（0xBF00）步长 */
  #define ARCH_PC_THUMB_BIT 1     /* 函数地址 bit0：Thumb 状态标记 */
  #define ARCH_CPSR_T_BIT   0x20  /* CPSR bit5：Thumb 状态位（PTRACE 写 PC
                                   * 不会自动同步 T 位，须显式置位） */
#else /* x86_64 */
  #define ARCH_MAX_INT_ARGS 6     /* SysV AMD64：rdi,rsi,rdx,rcx,r8,r9 */
  #define ARCH_MAX_CALL_ARGS 6    /* 参数上限 = 寄存器数（x86 栈传参与 call
                                     注入机制未实现，超限报错） */
  #define ARCH_STACK_ALIGN  16
  #define ARCH_LR_REGNO     0     /* x86 无 LR：返回地址压栈，注入机制不同 */
  #define ARCH_BP_PC_DELTA  1     /* INT3 停下时 RIP 指向断点指令之后 1 字节 */
  #define ARCH_BP_INSN_LEN  1     /* INT3 长度 */
  #define ARCH_NOP_STEP     4     /* 占位：x86 NOP 填充变长，槽位扫描未实现 */
#endif

/* ========================================================================
 * 2.5 寄存器快照结构类型别名 arch_regs_t
 *
 * aarch64 / x86_64：<sys/user.h> 提供 struct user_regs_struct；
 * 32 位 ARM 的 glibc <sys/user.h> 不提供该类型（编译时报
 * "struct user_regs_struct declared inside parameter list"），
 * 其寄存器快照由 struct user_regs（uregs[18]）承载——布局与内核
 * asm/ptrace.h 的 struct pt_regs 一致，PTRACE_GETREGSET 填充的就是
 * 该布局。业务代码统一使用 arch_regs_t，不得直接写架构专有类型名。
 * ======================================================================== */
#if defined(ARCH_ARM32)
typedef struct user_regs arch_regs_t;
#else
typedef struct user_regs_struct arch_regs_t;
#endif

/* ========================================================================
 * 3. 寄存器快照访问抽象
 *
 * arch_regs_t 布局随架构完全不同：
 *   aarch64: regs[31] + sp + pc + pstate
 *   arm32:   uregs[18]（r0..r12, sp, lr, pc, cpsr, orig_r0）
 *   x86_64:  r15..r9..rax..rip..rsp 等独立字段
 * 业务代码只能通过这些接口访问，字段差异全部收敛在 arch_port.c。
 * ======================================================================== */
unsigned long long arch_reg_pc(const arch_regs_t *r);
/* 写 PC（call 注入入口专用）。AArch64/x86_64：仅写 PC。
 * ARM32：指令集自适应——函数入口地址 bit0 置位则按 Thumb 进入，
 * bit0 缺失则继承寄存器快照现场 CPSR.T，据此设置/清除 CPSR.T 位，
 * 使 ARM 指令集函数同样可被 call 注入（不再假定全 Thumb-2）。 */
void arch_reg_set_pc(arch_regs_t *r, unsigned long long v);
unsigned long long arch_reg_sp(const arch_regs_t *r);
void arch_reg_set_sp(arch_regs_t *r, unsigned long long v);
unsigned long long arch_reg_arg(const arch_regs_t *r, int i);
void arch_reg_set_arg(arch_regs_t *r, int i, unsigned long long v);
unsigned long long arch_reg_ret(const arch_regs_t *r);
void arch_reg_set_lr(arch_regs_t *r, unsigned long long v);
/* 将断点槽位地址转换为注入 LR 的返回地址。ARM32 下恒置 bit0（=slot|1）：
 * 槽位返回断点是 Thumb BKPT，callee 无论 Thumb 还是 ARM 指令集，AAPCS
 * 的 bx lr / pop {pc} 返回都会按 LR bit0 interworking 切回 Thumb 命中
 * BKPT；非 interworking 手写返回（mov pc, lr）不支持。AArch64/x86 原样返回。 */
unsigned long long arch_ret_addr(unsigned long long addr);

/* ========================================================================
 * 4. ptrace 寄存器读写
 * ======================================================================== */
int arch_get_regs(pid_t pid, arch_regs_t *regs);
int arch_set_regs(pid_t pid, const arch_regs_t *regs);

/* ========================================================================
 * 5. 断点指令（注入调用的返回断点 trap）
 * ======================================================================== */
const unsigned char *arch_break_insn(void);   /* 断点指令字节流 */
int arch_break_insn_len(void);                /* 断点长度 */

/* ========================================================================
 * 6. NOP 填充槽位判断
 *
 * call 命令需在目标 .text 尾部 NOP 填充区放置返回断点；不同架构 NOP
 * 指令宽度不同（aarch64=4 字节，Thumb=2 字节），判断逻辑收敛于此。
 * ======================================================================== */
int arch_buf_is_nop(const unsigned char *buf, size_t off);

#endif /* ARCH_PORT_H */
