ARM v7 BKPT 指令在 Linux 2.6.35 上无法正常工作 [英] ARM v7 BKPT instruction doesn't work correctly on Linux 2.6.35

查看:51
本文介绍了ARM v7 BKPT 指令在 Linux 2.6.35 上无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个问题与 Linux 2.6.35 上的 ARM v7 上的 BKPT 指令有关.主要原因是故障指令(bkpt)的地址不正确,与ARM v7手册不对应.

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.

复制步骤如下:

  1. 将操作系统 SIGBUS 处理程序重新定义为我的 SIGBUS 处理程序:

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

  • 使用内联_asm并将BKPT"指令放入ma​​in()函数的代码中:

    int main(int argc, char **argv)  
    {
         InitSigBusHandler();
         __asm
         (
              "bkpt\n\t"
         );
    
         return 0;
    }
    

  • 这是我的 SIGBUS 处理程序:

  • 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));
    }   
    

  • 问题是指令(bkpt)的故障地址错误,不符合ARM v7规范.这是程序运行后的控制台输出:

  • 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:

    在 SIGBUS 处理程序中:
    故障地址:86b0
    在 SIGBUS 处理程序中:
    故障地址:86c0
    在 SIGBUS 处理程序中:
    故障地址:86c0
    在 SIGBUS 处理程序中:
    故障地址:86c0
    在 SIGBUS 处理程序中:
    故障地址:86c0
    在 SIGBUS 处理程序中:
    故障地址:86b0
    在 SIGBUS 处理程序中:
    故障地址:86a8
    在 SIGBUS 处理程序中:
    故障地址:86f0

    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

  • 在 x86 架构上,此示例可以正常工作.在 ARM v7 架构上,这个示例有一个奇怪的行为.

    On x86 architecture this sample works correctly. On ARM v7 architecture this sample has a strange behavior.

    如果我在 ARM v7 上使用 GDB,他会用正确的地址捕获我的 BKPT 指令.

    If I use GDB on ARM v7, he catches my BKPT instruction with correct address.

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

    Maybe someone knows what I do wrong ?

    推荐答案

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

    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.

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

    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.

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

    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;
    

    并且您希望 si_addr 中的故障地址为 0x1234567890abcdef.情况并非如此,因为访问该地址将创建 #GPF 而不是 #PF 错误,并且前者不会在 x86 上设置错误地址寄存器.如果您查看保存为 ucontext_t/struct sigcontext(嵌入在其中)一部分的程序计数器,您会看到错误指令地址,这将是精确的.

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

    如上所述,问题在于确定 CPU 寄存器状态必然会给您提供依赖于 CPU 的代码.您必须进行一些改编/包装以保持这种可移植性,例如:

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

    希望有所帮助!

    这篇关于ARM v7 BKPT 指令在 Linux 2.6.35 上无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆