假设si_addr
is precise断点陷阱(即故障发生时操作的实际地址)不一定是真实的/可移植的。
您确实需要检查保存的寄存器状态,即信号处理程序的第三个参数,可以将其转换为ucontext_t*
。该州是不便携CPU 之间的通用接口仅传递void *
; GDB 检查它(以便info registers
有效)并从那里提取错误的程序计数器,这就是为什么它能够将您指向断点指令。
如果您尝试过,您在 ARM 上遇到的情况与在 64 位 x86 上遇到的情况类似:
volatile char *ptr = (char*)0x1234567890abcdef;
char crashme = *ptr;
并且您期望故障地址为si_addr
to be 0x1234567890abcdef
。情况并非如此,因为访问该地址将创建一个#GPF
not #PF
故障,前者没有在x86上设置故障地址寄存器。如果您查看保存为一部分的程序计数器ucontext_t
/ struct sigcontext
(嵌入在那里)你会看到错误的指令地址,而且这是准确的。
将您的信号处理程序更改为:
void SigBusHandler(
int signum,
siginfo_t *pAct,
void *context
)
{
struct sigcontext *ctx = &(((ucontext_t*)context)->uc_mcontext);
uintptr_t fault_address = ctx->arm_pc; /* that's what you'll see on ARM */
...
}
如前所述,问题在于弄清楚 CPU 寄存器状态必然会为您提供依赖于 CPU 的代码。您必须进行一些调整/包装才能保持其便携性,例如:
#if defined(ARM)
#define GET_PC_FROM_CONTEXT(c) (((ucontext_t *)(c))->uc_mcontext.arm_pc)
#elsif defined(__i386__)
define GET_PC_FROM_CONTEXT(c) (((ucontext_t *)(c))->uc_mcontext.eip)
#elsif defined(__amd64__)
define GET_PC_FROM_CONTEXT(c) (((ucontext_t *)(c))->uc_mcontext.rip)
#endif
uintptr_t instr_address = GET_PC_FROM_CONTEXT(context);
希望有帮助!