Ho sviluppato una libreria che gestisce i segnali SIGILL. Dal momento che voglio evitare la dipendenza da libc e usare direttamente le syscalls di Linux. Ho notato che la mia libreria si blocca su alcuni sistemi Linux, e dopo un sacco di debug ho scoperto che usare il syscall rt_sigaction
invece di sigaction
risolve il problema. Tuttavia, non ho trovato una descrizione della differenza tra le due syscalls. Qualcuno su SO conosce i dettagli sottostanti?Qual è la differenza tra syscalls segnale e rt_signal in Linux?
Aggiornamento: utilizzo i gestori di segnale per rilevare il supporto CPU per alcune estensioni dell'istruzione ARM, ad es. Istruzione XScale MIATT
. Qui è la funzione di tastatura istruzione:
static uint32_t probe_xscale() {
register uint32_t retValue asm("r0") = 0;
asm volatile (
// Equivalent of the following code:
// ".arch xscale\n"
// "MIATT acc0, r0, r0;"
// If the next line raises SIGILL, the signal handle will change r0 to 1 and skip the instruction (4 bytes)
"MCR P0, 0x1, r0, c15, c0, 0;"
: "+r" (retValue)
:
:
);
return retValue;
}
Nel gestore SIGILL avanzo registro PC
da 4 byte (dimensione di questa istruzione), e cambiare uno dei registri per indicare che gestore SIGILL stato chiamato. Ecco il codice del gestore di segnale.
static void probe_signal_handler(int, siginfo_t *, void* ptr) {
ucontext_t* ctx = (ucontext_t*)ptr;
ctx->uc_mcontext.arm_pc += 4;
ctx->uc_mcontext.arm_r0 = 1;
}
Ecco come faccio io il sondaggio (la funzione restituisce 0 se l'istruzione non ha causato SIGILL, 1 se handler SIGILL è stato chiamato, e 2 se sigaction syscall non):
static uint32_t probeInstruction(uint32_t (*ProbeFunction)()) {
struct sigaction oldSigillAction;
struct sigaction probeSigillAction;
memset(&probeSigillAction, 0, sizeof(probeSigillAction));
probeSigillAction.sa_sigaction = &probe_signal_handler;
// Needs Linux >= 2.2
probeSigillAction.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
int sigactionResult = _syscall_sigaction(SIGILL, &probeSigillAction, &oldSigillAction);
if (sigactionResult == 0) {
const uint32_t probeResult = ProbeFunction();
_syscall_sigaction(SIGILL, &oldSigillAction, NULL);
return probeResult;
} else {
return 2;
}
}
Qui è la mia implementazione della funzione syscall stub sigaction:
static int _syscall_sigaction(int signum, const struct sigaction *new_action, struct sigaction *old_action) __attribute__((noinline));
static int _syscall_sigaction(int signalNumberParameter, const struct sigaction *newActionParameter, struct sigaction *oldActionParameter) {
register int result asm ("r0");
register int signalNumber asm ("r0") = signalNumberParameter;
register const struct sigaction *newAction asm ("r1") = newActionParameter;
register struct sigaction *oldAction asm ("r2") = oldActionParameter;
register int syscallNumber asm ("r7") = __NR_rt_sigaction;
asm volatile (
"swi $0;"
: "=r" (result)
: "r" (signalNumber), "r" (newAction), "r" (oldAction), "r" (syscallNumber)
:
);
return result;
}
ho provato questo codice nell'emulatore da Android SDK (QEMU), e sulla PandaBoard con Ubuntu. Nell'emulatore il codice funziona bene (sia quando si emulano CPU ARM9 e Cortex-A8), ma su Pandaboard si blocca su istruzione MIATT se si utilizza __NR_sigaction: sembra che dopo il gestore di segnale il codice non salti 4 byte, ma funzioni la stessa istruzione
Sto indovinando che la versione 'rt_sigaction' è una versione" in tempo reale ". Significa che è progettato per avere un tempo di chiamata deterministico. –
Questi sono quasi esattamente lo stesso codice, entrambi atterrano su do_sigaction() nel kernel. Se hai problemi, probabilmente ti aiuterà a dettagliarli. –
Ho aggiunto più dettagli e parte rilevante del mio codice alla domanda. –