ARM v7 BKPT 指令在 Linux 2.6.35 上无法正常工作
我遇到的问题是与 Linux 2.6.35 上的 ARM v7 上的 BKPT 指令连接。主要原因是故障指令(bkpt)的地址不正确,与ARM v7手册不对应。
以下是重现步骤:
将操作系统 SIGBUS 处理程序重新定义为我的 SIGBUS 处理程序:
void InitSigBusHandler() { 结构 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” ); 返回0; }
这是我的 SIGBUS 处理程序:
void SigBusHandler( 整型符号, siginfo_t *pAct, void *pOldAct ) { 写(2, (const char *)MSG_SIGBUS_IN_HANDLER, strlen((const char *)MSG_SIGBUS_IN_HANDLER) ); uint32_t failureAddr = (uint32_t)pAct->si_addr; memcpy((void *)缓冲区, (无效*)MSG_SIGBUS_FAULT_ADDR, strlen(MSG_SIGBUS_FAULT_ADDR) ); 写(2, (const char *)MSG_SIGBUS_FAULT_ADDR, strlen((const char *)MSG_SIGBUS_FAULT_ADDR) ); sprintf(缓冲区,“%x\n”,faultAddr); 写(2,缓冲区,strlen(缓冲区)); }
问题是指令 (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 指令。
也许有人知道我做错了什么?
I have a problem is connected with BKPT instruction on ARM v7 on Linux 2.6.35. The main reason is that the address of fault instruction (bkpt) is not correct and does not correspond to ARM v7 manual.
Here is the steps for reproducing:
Redefine OS SIGBUS handler to my SIGBUS handler:
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); }
Use the inline _asm and put the "BKPT" instruction into code in main() function:
int main(int argc, char **argv) { InitSigBusHandler(); __asm ( "bkpt\n\t" ); return 0; }
Here is my SIGBUS handler:
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)); }
The problem is the fault adress of instruction (bkpt) is wrong and does not correspond to ARM v7 specification. Here is the console output after the program worked:
In SIGBUS handler:
Fault Address: 86b0
In SIGBUS handler:
Fault Address: 86c0
In SIGBUS handler:
Fault Address: 86c0
In SIGBUS handler:
Fault Address: 86c0
In SIGBUS handler:
Fault Address: 86c0
In SIGBUS handler:
Fault Address: 86b0
In SIGBUS handler:
Fault Address: 86a8
In SIGBUS handler:
Fault Address: 86f0
On x86 architecture this sample works correctly. On ARM v7 architecture this sample has a strange behavior.
If I use GDB on ARM v7, he catches my BKPT instruction with correct address.
Maybe someone knows what I do wrong ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
对于断点陷阱来说,
si_addr
的假设精确(即发生故障时操作的实际地址)不一定是正确的/可移植的。您确实需要检查保存的寄存器状态,即信号处理程序的第三个参数,它可以转换为
ucontext_t*
。状态在 CPU 之间不可移植,因此通用接口仅传递void *
; GDB 检查它(以便信息寄存器
工作)并从那里提取错误的程序计数器,这就是为什么它能够将您指向断点指令。如果您尝试过,您在 ARM 上遇到的情况与在 64 位 x86 上遇到的情况类似:
并且您预计
si_addr
中的故障地址为0x1234567890abcdef
。情况并非如此,因为访问此地址时将创建#GPF
而不是#PF
故障,并且前者不会在 x86 上设置故障地址寄存器。如果您查看保存为ucontext_t
/struct sigcontext
(嵌入其中)的一部分的程序计数器,您将看到错误的指令地址,而且这将是精确的。将信号处理程序更改为:
如前所述,问题在于弄清楚 CPU 寄存器状态必然会为您提供依赖于 CPU 的代码。您必须进行一些调整/包装才能保持其便携性,例如:
希望有帮助!
The assumption that
si_addr
is precise (i.e. the actual address operated on when the fault occurred) for a breakpoint trap is not necessarily true / portable.You do need to inspect the saved register state, i.e. the third parameter to the signal handler, which can be cast to
ucontext_t*
. The state is not portable between CPUs and hence the generic interface only passes avoid *
; GDB inspects it (so thatinfo registers
works) and extracts the program counter of the fault from there, that's why it's able to point you to the breakpoint instruction.The situation you're encountering on ARM here is similar as to what you'd get on 64bit x86 if you tried:
and you expect the fault address in
si_addr
to be0x1234567890abcdef
. That won't be the case because this address on access will create a#GPF
not#PF
fault, and the former doesn't set the fault address register on x86. If you look into the program counter saved as part ofucontext_t
/struct sigcontext
(embedded in there) you'll see the faulting instruction address though, and that'll be precise.Change your signal handler to:
Problem, as said, is that figuring out CPU register state necessarily gives you CPU-dependent code. You'll have to make some adaptations / wrappers to keep this portable, like:
Hope that helps !