我有一个问题是在Linux 2.6.35上与ARM v7上的BKPT指令相关联。主要原因是故障指令(bkpt)的地址不正确,与ARM v7手册不对应。
以下是复制的步骤:
将OS SIGBUS处理程序重新定义为我的SIGBUS处理程序:
void InitSigBusHandler() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);
sa.sa_sigaction = SigBusHandler;
sigaction(SIGBUS, &sa, NULL);
}
使用内联_asm并将“BKPT”指令放入 main()函数中的代码中:
int main(int argc, char **argv)
{
InitSigBusHandler();
__asm
(
"bkpt\n\t"
);
return 0;
}
这是我的SIGBUS处理程序:
void SigBusHandler(
int signum,
siginfo_t *pAct,
void *pOldAct
)
{
write(2,
(const char *)MSG_SIGBUS_IN_HANDLER,
strlen((const char *)MSG_SIGBUS_IN_HANDLER)
);
uint32_t faultAddr = (uint32_t)pAct->si_addr;
memcpy((void *)buffer,
(void *)MSG_SIGBUS_FAULT_ADDR,
strlen(MSG_SIGBUS_FAULT_ADDR)
);
write(2,
(const char *)MSG_SIGBUS_FAULT_ADDR,
strlen((const char *)MSG_SIGBUS_FAULT_ADDR)
);
sprintf(buffer, "%x\n", faultAddr);
write(2, buffer, strlen(buffer));
}
问题是指令的故障地址(bkpt)是错误的,并不符合ARM v7规范。这是程序工作后的控制台输出:
在SIGBUS处理程序中:
故障地址:86b0
在SIGBUS处理程序中:
故障地址:86c0
在SIGBUS处理程序中:
故障地址:86c0
在SIGBUS处理程序中:
故障地址:86c0
在SIGBUS处理程序中:
故障地址:86c0
在SIGBUS处理程序中:
故障地址:86b0
在SIGBUS处理程序中:
故障地址:86a8
在SIGBUS处理程序中:
故障地址:86f0
在x86架构上,此示例可正常工作。在ARM v7架构上,此示例具有奇怪的行为。
如果我在ARM v7上使用GDB,他会使用正确的地址捕获我的BKPT指令。
也许有人知道我做错了什么?
答案 0 :(得分:4)
断点陷阱si_addr
精确(即故障发生时的实际地址)的假设不一定是真的/可移植的。
您需要检查已保存的寄存器状态,即信号处理程序的第三个参数,可以强制转换为ucontext_t*
。状态在CPU之间不可移植,因此通用接口只传递void *
; GDB检查它(以便info registers
工作)并从那里提取故障的程序计数器,这就是它能够指向断点指令的原因。
您在ARM上遇到的情况与您尝试在64位x86上获得的情况类似:
volatile char *ptr = (char*)0x1234567890abcdef;
char crashme = *ptr;
并且您希望si_addr
中的错误地址为0x1234567890abcdef
。情况并非如此,因为访问时该地址将创建#GPF
而非#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);
希望有所帮助!