2011-05-11 60 views
3

我在Linux 2.6.35上遇到了與ARM v7上的BKPT指令相關的問題。主要原因是故障指令地址(bkpt)不正確,並且不符合ARM v7手冊。ARM v7 BKPT指令在Linux 2.6.35上無法正常工作

這裏是再現步驟:

  1. 重新定義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); 
        } 
    
  2. 使用內聯_asm,把 「BKPT」 指令到代碼主要()功能:

    int main(int argc, char **argv) 
    { 
        InitSigBusHandler(); 
        __asm 
        (
          "bkpt\n\t" 
        ); 
    
        return 0; 
    } 
    
  3. 這裏是我的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)); 
    } 
    
  4. 的問題是指令(BKPT)的故障ADRESS是錯誤的,不符合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指令和正確的地址。

也許有人知道我做錯了什麼?

回答

4

假設si_addr精確(即故障發生時操作的實際地址)的斷點陷阱不一定是真實/便攜式的。

您確實需要檢查保存的寄存器狀態,即信號處理程序的第三個參數,該參數可以轉換爲ucontext_t*。在CPU之間的狀態是不可移植,因此通用接口只通過void *; GDB檢查它(以便info registers工作)並從那裏提取故障的程序計數器,這就是爲什麼它能夠指向斷點指令。

你在這裏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); 

希望幫助!

+0

謝謝!這是有幫助的。 –