ARM v7 BKPT 指令在 Linux 2.6.35 上无法正常工作

发布于 2024-11-06 15:18:30 字数 1829 浏览 0 评论 0原文

我遇到的问题是与 Linux 2.6.35 上的 ARM v7 上的 BKPT 指令连接。主要原因是故障指令(bkpt)的地址不正确,与ARM v7手册不对应。

以下是重现步骤:

  1. 将操作系统 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);
        } 
    
  2. 使用内联 _asm 并将“BKPT”指令放入 ma​​in() 函数中的代码中:

    int main(int argc, char **argv)  
    {
         InitSigBusHandler();
         __asm
         (
              “bkpt\n\t”
         );
    
         返回0;
    }
    
  3. 这是我的 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(缓冲区));
    }   
    
  4. 问题是指令 (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:

  1. 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);
        } 
    
  2. 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;
    }
    
  3. 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));
    }   
    
  4. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

最好是你 2024-11-13 15:18:30

对于断点陷阱来说,si_addr 的假设精确(即发生故障时操作的实际地址)不一定是正确的/可移植的。

您确实需要检查保存的寄存器状态,即信号处理程序的第三个参数,它可以转换为ucontext_t*。状态在 CPU 之间不可移植,因此通用接口仅传递 void *; GDB 检查它(以便信息寄存器 工作)并从那里提取错误的程序计数器,这就是为什么它能够将您指向断点指令。

如果您尝试过,您在 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);

希望有帮助!

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 a void *; GDB inspects it (so that info 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:

volatile char *ptr = (char*)0x1234567890abcdef;
char crashme = *ptr;

and you expect the fault address in si_addr to be 0x1234567890abcdef. 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 of ucontext_t / struct sigcontext (embedded in there) you'll see the faulting instruction address though, and that'll be precise.

Change your signal handler to:

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 */
    ...
}

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:

#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);

Hope that helps !

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文