Няколко извиквания към манипулатора на sigtrap не работят

Имам малко приложение, написано на 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;
}

person ayylmao    schedule 21.09.2015    source източник
comment
Чакай, значи приемаш сигнал, изопачаваш контролния поток, като скачаш направо към кода си в контекста на манипулатора на сигнали и се опитваш рекурсивно да обработваш повече сигнали, без изобщо да се връщаш? Няма да твърдя, че съм експерт по сигналите, но това не изглежда правилно...   -  person Notlikethat    schedule 21.09.2015
comment
@Notlikethat: Толкова е лошо дори недефинираното поведение от извикване на printf() в манипулатор на сигнали е кротко в сравнение.   -  person EOF    schedule 22.09.2015
comment
sigsetjmp() и siglongjmp() е по-вероятно да успеят във всичко, близко до това, което се опитвате да направите.   -  person Andrew Henle    schedule 22.09.2015
comment
Тези въпроси и отговори изглежда засягат всичко, освен действителното модифициране на запазения контекст.   -  person Notlikethat    schedule 22.09.2015
comment
@notlikethat: благодаря, намерих това, което търсех. Можете да посочите: struct sigcontext* context = &(((ucontext_t*)ptr)->uc_mcontext); uintptr_t fault_address = context->arm_pc; За да получите точен адрес за подаване на сигнал (GDB проверява състоянието на регистъра по този начин). И можете да зададете context->arm_pc += 4 да постигна това, което искам, без да причинявам неправилно поведение поради неправилно връщане от манипулатора на сигнали.   -  person ayylmao    schedule 22.09.2015
comment
Ядрото все още ще вярва, че обработвате сигнал и ще получите суроватка „сигнал в сигнал“, като използвате първоначалния си подход (да кажем множество sigbus и т.н.), така че ядрото смята, че процесът ви е извън контрол. ucontext_t е правилният метод за промяна на потока при връщане от манипулатора на сигнали.   -  person artless noise    schedule 22.09.2015