Предположение, что 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);
Надеюсь, это поможет!