Инструкция ARM v7 BKPT не работает правильно в Linux 2.6.35 - PullRequest
3 голосов
/ 11 мая 2011

У меня проблема связана с инструкцией BKPT на ARM v7 на Linux 2.6.35.Основная причина заключается в том, что адрес инструкции по неисправности (bkpt) неверен и не соответствует руководству ARM v7.

Вот шаги для воспроизведения:

  1. Переопределить обработчик 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" в код функции main () :

    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) неверен и не соответствует спецификации 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 с правильным адресом.

Может кто знает, что я делаю не так?

1 Ответ

4 голосов
/ 11 мая 2011

Предположение, что si_addr является точным (т. Е. Фактический адрес, использованный при возникновении ошибки) для прерывания точки останова, не обязательно является истинным / переносимым.

Вам необходимо проверить сохраненное состояние регистра, то есть третий параметр обработчика сигнала, который может быть приведен к ucontext_t*. Состояние не является переносимым между процессорами, и, следовательно, общий интерфейс передает только void *; GDB проверяет его (так что info registers работает) и извлекает оттуда программный счетчик сбоя, поэтому он может указать вам инструкцию точки останова.

Ситуация, с которой вы сталкиваетесь в ARM, аналогична ситуации, которую вы получите на 64-битной x86, если попытаетесь:

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

и вы ожидаете, что адрес ошибки в si_addr будет 0x1234567890abcdef. Это не будет иметь место, потому что этот адрес при доступе создаст ошибку #GPF not #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 */
    ...
}

Проблема, как уже было сказано, состоит в том, что вычисление состояния регистра ЦП обязательно дает вам зависящий от ЦП код. Вы должны будете сделать некоторые изменения / обертки, чтобы сохранить этот портативный, как:

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

Надеюсь, это поможет!

...