Имам малко приложение, написано на C за Linux на ARM, което настройва манипулатор на сигнала за сигнала SIGTRAP и трябва да улови инструкцията bkpt
(чрез кода TRAP_HWBKPT
) и ефективно да я пропусне.
Манипулаторът просто уведомява откъде е била повдигната точката на прекъсване и използва вграден епилог, за да пренасочи кодовия поток към 4 байта (поради RISC архитектурата на ARM) след адреса на точката на прекъсване (което е следващата инструкция). Знам, че все още трябва да има някакво записване на регистър, но това не е проблемът за сега.
Това, което изглежда се случва, е, че първото извикване на функция, съдържаща точка на прекъсване, върви добре, но второто извикване вече не се улавя от персонализирания sigtrap_handler
и вместо това се предава на манипулатора по подразбиране (причинявайки прекратяване на процеса):
Някой знае ли защо се случва това и как да го поправя, за да мога да постигна целта да извикам манипулатора за всяка инструкция bkpt
и да продължа изпълнението както обикновено?
$ gcc -o break break.c
$ ./break
START
ENTER [0]!
SIGTRAP at 0x856c
EXIT [0]!
ENTER [1]!
Trace/breakpoint trap
Това е въпросният код:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifndef TRAP_BRANCH
#define TRAP_BRANCH 3
#endif
#ifndef TRAP_HWBKPT
#define TRAP_HWBKPT 4
#endif
static void sigtrap_handler(int sig, siginfo_t* siginfo, void* ptr)
{
switch(siginfo->si_code)
{
case TRAP_HWBKPT:
{
printf("SIGTRAP at %p\n", (void*)siginfo->si_addr);
asm volatile("sub sp, r11, #4\n\t"
"pop {r11}\n\t"
"sub sp, #4\n\t"
"mov pc, %0"
:
: "r" (siginfo->si_addr + 4) // Proceed to instruction directly after breakpoint
);
}break;
// misc. SIGTRAP codes (fallthrough)
case TRAP_BRKPT:
case TRAP_TRACE:
case TRAP_BRANCH:
default:
{
exit(-1);
}break;
}
}
void dummy_routine(int n)
{
printf("ENTER [%d]!\n", n);
// Breakpoint
asm volatile("bkpt");
printf("EXIT [%d]!\n", n);
return;
}
int main (int argc, char *argv[])
{
int i;
struct sigaction act;
// Set up sigtrap handler
memset (&act, 0, sizeof(act));
act.sa_sigaction = sigtrap_handler;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGTRAP, &act, 0)) {
perror("Error: sigaction");
return 1;
}
printf("START\n");
// Trigger routine containing breakpoint multiple times
for(i = 0; i < 2; i++)
{
dummy_routine(i);
}
printf("END\n");
return 0;
}
printf()
в манипулатор на сигнали е кротко в сравнение. - person EOF   schedule 22.09.2015sigsetjmp()
иsiglongjmp()
е по-вероятно да успеят във всичко, близко до това, което се опитвате да направите. - person Andrew Henle   schedule 22.09.2015struct sigcontext* context = &(((ucontext_t*)ptr)->uc_mcontext); uintptr_t fault_address = context->arm_pc;
За да получите точен адрес за подаване на сигнал (GDB проверява състоянието на регистъра по този начин). И можете да зададетеcontext->arm_pc += 4
да постигна това, което искам, без да причинявам неправилно поведение поради неправилно връщане от манипулатора на сигнали. - person ayylmao   schedule 22.09.2015ucontext_t
е правилният метод за промяна на потока при връщане от манипулатора на сигнали. - person artless noise   schedule 22.09.2015