我开发了一个处理 SIGILL 信号的库。因为我想避免 libc 依赖,直接使用 Linux 系统调用。我注意到我的库挂在一些 Linux 系统上,经过大量调试后,我发现使用rt_sigaction
syscall 代替了sigaction
解决问题。但是,我没有找到两个系统调用之间差异的描述。SO上有没有人知道基本细节?
更新:我使用信号处理程序来检测 CPU 对某些 ARM 指令扩展的支持,例如 XScale 指令MIATT
。这是指令探测功能:
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;
}
在 SIGILL 处理程序中,我将PC
寄存器提前 4 个字节(该指令的大小),并更改其中一个寄存器以指示调用了 SIGILL 处理程序。这是信号处理程序代码。
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;
}
这是我进行探测的方法(如果指令没有导致 SIGILL,则函数返回 0,如果调用了 SIGILL 处理程序,则返回 1,如果 sigaction 系统调用失败,则返回 2):
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;
}
}
这是我对 sigaction syscall 存根函数的实现:
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;
}
我在 Android SDK (qemu) 的模拟器和运行 Ubuntu 的 Pandaboard 上测试了这段代码。在模拟器中,代码运行良好(在模拟 ARM9 和 Cortex-A8 CPU 时),但在 Pandaboard 上,如果我使用 __NR_sigaction,它会挂在 MIATT 指令上:似乎在信号处理程序之后代码不会跳过 4 个字节,而是运行相同的指令。