ARM v7 BKPT指令不';t无法在Linux 2.6.35上正常工作

ARM v7 BKPT指令不';t无法在Linux 2.6.35上正常工作,linux,arm,Linux,Arm,我有一个问题与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; sigf

我有一个问题与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体系结构上,此示例正常工作。在ARMV7架构上,此示例有一个奇怪的行为

    如果我在ARMV7上使用GDB,他会捕捉到带有正确地址的BKPT指令


    也许有人知道我做错了什么?

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

    您确实需要检查保存的寄存器状态,即信号处理程序的第三个参数,它可以转换为
    ucontext\u t*
    。该状态在CPU之间不可移植,因此通用接口只传递一个
    void*
    ;GDB检查它(以便
    info registers
    工作)并从中提取故障的程序计数器,这就是为什么它能够将您指向断点指令的原因

    您在ARM上遇到的情况与64位x86上遇到的情况类似,如果您尝试:

    volatile char *ptr = (char*)0x1234567890abcdef;
    char crashme = *ptr;
    
    您希望
    si_addr
    中的故障地址为
    0x1234567890abcdef
    。情况并非如此,因为此访问地址将创建一个
    #GPF
    #PF
    故障,并且前者不会在x86上设置故障地址寄存器。如果您查看保存为
    ucontext\u 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);
    
    希望有帮助