Marat Dukhan Marat Dukhan - 6 months ago 16
Linux Question

What is the difference between signal and rt_signal syscalls in Linux?

I developed a library which handles SIGILL signals. Since I want to avoid libc dependence, and use Linux syscalls directly. I noticed that my library hangs on some Linux systems, and after a lot of debugging I found that using

rt_sigaction
syscall instead of
sigaction
solves the problem. However, I didn't find a description of the difference between the two syscalls. Does anyone on SO know the underlying details?

Update: I use signal handlers to detect CPU support for some ARM instruction extensions, e.g. XScale instruction
MIATT
. Here is the instruction probing function:

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;
}


In the SIGILL handler I advance the
PC
register by 4 bytes (size of this instruction), and change one of the registers to indicate that SIGILL handler was called. Here is the signal handler code.

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;
}


Here is how I do the probing (the function returns 0 if the instruction did not cause SIGILL, 1 if SIGILL handler was called, and 2 if sigaction syscall failed):

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;
}
}


Here is my implementation of sigaction syscall stub function:

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;
}


I tested this code in the emulator from Android SDK (qemu), and on Pandaboard running Ubuntu. In the emulator the code runs well (both when emulating ARM9 and Cortex-A8 CPUs), but on Pandaboard it hangs on MIATT instruction if I use __NR_sigaction: it seems that after the signal handler the code doesn't skip 4 bytes, but runs the same instruction.

Answer

I don't have a definite answer, but I will still try to contribute:

Looking at the kernel source:

 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}

The differance as I see it is that rt_sigaction copies the entire sigaction struct, while sigaction is getting and altering the memory inline (using the get/set user functions)... I'm not sure, but maybe it takes more time to access the userspace memory directly rather than work with a temporary copy.