ARM V7 BKPT指令不会在Linux 2.6.35正常工作 [英] ARM v7 BKPT instruction doesn't work correctly on Linux 2.6.35

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

问题描述

我有一个问题与在Linux 2.6.35 ARM V7 BKPT指令连接。主要的原因是断层指令(BKPT)的地址不正确,不符合ARM V7手册。

下面是步骤用于再现:


  1. 重新定义操​​作系统SIGBUS处理程序,以我的SIGBUS处理程序:

     无效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指令到code在的main()功能:

      INT主(INT ARGC,字符** argv的)
    {
         InitSigBusHandler();
         __asm
         (
              BKPT \\ n \\ t的
         );     返回0;
    }


  3. 下面是我的SIGBUS处理程序:

     无效SigBusHandler(
        INT正负号,
        siginfo_t * PACT,
        无效* pOldAct
        )
    {
        写(2,
             (为const char *)MSG_SIGBUS_IN_HANDLER,
              strlen的((为const char *)MSG_SIGBUS_IN_HANDLER)
              );    uint32_t的FAULTADDR =(uint32_t的)pAct-> si_addr;
        的memcpy((无效*)缓冲区,
              (无效*)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)的故障ADRESS是错误的,不符合ARM V7规范。这里是控制台输出的程序工作后:


      

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



在x86架构此示例正常工作。在ARM V7架构此示例有一个奇怪的行为。

如果我用GDB的ARM V7,他抓住我的正确地址BKPT指令。

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


解决方案

假设有 si_addr precise 的(即实际在发生故障时,为断点陷阱操作)地址不一定是真实/便携。

您确实需要检查保存的寄存器状态,即第三个参数的信号处理程序,它可以转换为 ucontext_t * 。国家是的无法移植的CPU之间,因此通用接口只传递一个无效* ; GDB检查它(让信息登记作品),并提取从那里故障的程序计数器,这就是为什么它能够给你指向断点指令。

你在这里ARM遇到的情况是,以你在64位的x86得到什么,如果你尝试过类似的:

 挥发性的char * PTR =(字符*)0x1234567890abcdef;
焦炭crashme = * PTR;

和你期望在 si_addr 故障地址为 0x1234567890abcdef 。这不会是这种情况,因为在访问该地址将创建一个 #GPF 不是 #PF 故障,以及前者不设置故障地址寄存器在x86。如果你看看保存为的一部分ucontext_t / 结构sigcontext 程序计数器(内嵌在那里)你会看到错误指令的地址,虽然,那将是precise。

信号处理程序更改为:

 无效SigBusHandler(
     INT正负号,
     siginfo_t * PACT,
     void *的背景下
    )
{
    结构sigcontext * CTX =及(((ucontext_t *)上下文) - > uc_mcontext);
    uintptr_t形式fault_address =则将ctx-> arm_pc; / *这就是你会在ARM看到* /
    ...
}

问题,因为说的是搞清楚CPU寄存器状态,一定让你的CPU依赖性code。你必须做出一些调整/包装保持这种便携式,如:

 #如果定义(ARM)
的#define GET_PC_FROM_CONTEXT(C)(((ucontext_t *)(三)) - > uc_mcontext.arm_pc)
#elsif定义(__ i386__)
定义GET_PC_FROM_CONTEXT(C)(((ucontext_t *)(三)) - > uc_mcontext.eip)
#elsif定义(__ amd64__)
定义GET_PC_FROM_CONTEXT(C)(((ucontext_t *)(三)) - > uc_mcontext.rip)
#万一uintptr_t形式instr_address = GET_PC_FROM_CONTEXT(背景);

希望帮助!

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 ?

解决方案

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 !

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

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