2012-10-15 4 views
8

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

+0

Sto indovinando che la versione 'rt_sigaction' è una versione" in tempo reale ". Significa che è progettato per avere un tempo di chiamata deterministico. –

+0

Questi sono quasi esattamente lo stesso codice, entrambi atterrano su do_sigaction() nel kernel. Se hai problemi, probabilmente ti aiuterà a dettagliarli. –

+0

Ho aggiunto più dettagli e parte rilevante del mio codice alla domanda. –

risposta

1

Da man sigaction (link) cito:

La chiamata originaria sistema Linux è stato chiamato sigaction(). Tuttavia, con l'aggiunta di segnali in tempo reale in Linux 2.2, il tipo sigset_t a 32 bit a dimensione fissa, supportato da quella chiamata di sistema non era più adatto allo scopo . Di conseguenza, una nuova chiamata di sistema, rt_sigaction(), è stata aggiunta a per supportare un tipo sigset_t ingrandito. La nuova chiamata di sistema accetta un quarto argomento, size_t sigsetsize, che specifica la dimensione in byte dei set di segnali in act.sa_mask e oldact.sa_mask.

+0

Anche da" man sigaction ", se stai usando glibc, non c'è bisogno di usare rt_sigaction. "La funzione wrapper glibc sigaction() nasconde questi dettagli da noi, chiamando in modo trasparente rt_sigaction() quando il kernel lo fornisce." – cooperised

4

Non ho una risposta definitiva, ma sarò comunque cercare di contribuire:

Guardando il sorgente del kernel:

300SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act, 
301  struct sigaction __user *, oact) 
302{ 
303  struct k_sigaction new_ka, old_ka; 
304  int ret; 
305  int err = 0; 
306 
307  if (act) { 
308    old_sigset_t mask; 
309 
310    if (!access_ok(VERIFY_READ, act, sizeof(*act))) 
311      return -EFAULT; 
312    err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); 
313    err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); 
314    err |= __get_user(mask, &act->sa_mask.sig[0]); 
315    if (err) 
316      return -EFAULT; 
317 
318    siginitset(&new_ka.sa.sa_mask, mask); 
319  } 
320 
321  ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); 
322 
323  if (!ret && oact) { 
324    if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) 
325      return -EFAULT; 
326    err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); 
327    err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); 
328    err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); 
329    err |= __put_user(0, &oact->sa_mask.sig[1]); 
330    err |= __put_user(0, &oact->sa_mask.sig[2]); 
331    err |= __put_user(0, &oact->sa_mask.sig[3]); 
332    if (err) 
333      return -EFAULT; 
334  } 
335 
336  return ret; 
337} 
338#endif 

vs.

2955SYSCALL_DEFINE4(rt_sigaction, int, sig, 
2956    const struct sigaction __user *, act, 
2957    struct sigaction __user *, oact, 
2958    size_t, sigsetsize) 
2959{ 
2960  struct k_sigaction new_sa, old_sa; 
2961  int ret = -EINVAL; 
2962 
2963  /* XXX: Don't preclude handling different sized sigset_t's. */ 
2964  if (sigsetsize != sizeof(sigset_t)) 
2965    goto out; 
2966 
2967  if (act) { 
2968    if (copy_from_user(&new_sa.sa, act, sizeof(new_sa.sa))) 
2969      return -EFAULT; 
2970  } 
2971 
2972  ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL); 
2973 
2974  if (!ret && oact) { 
2975    if (copy_to_user(oact, &old_sa.sa, sizeof(old_sa.sa))) 
2976      return -EFAULT; 
2977  } 
2978out: 
2979  return ret; 
2980} 

Il differance come Vedo che rt_sigaction copia l'intera struttura di sigaction, mentre sigaction sta ottenendo e alterando la memoria inline (usando le funzioni get/set dell'utente) ... Non sono sicuro, ma forse ci vuole più tempo accedere direttamente alla memoria dello spazio utente piuttosto che lavorare con una copia temporanea.