如何判断SIGILL是由非法指令还是kill -ILL引起的?

4
在通过void (*sa_sigaction)(int, siginfo_t *, void *);安装的信号处理程序中,我如何判断SIGILL是由非法指令还是某个进程发送SIGILL引起的?我查看了siginfo_t的si_pid,但在遇到非法指令时,它似乎未初始化,因此我无法根据它来做出决定。当然,我正在寻找一个简单且可移植的解决方案,而不是读取si_addr处的指令代码并尝试确定其是否合法。

2
你为什么想要这样做? - ctn
为了找到Xenomai线程终止的原因,其中gdb显示“程序以信号4非法指令终止”,但在$pc <pthread_setcanceltype@plt>处,在核心文件中有一个合法的分支指令。 - Armali
1个回答

8

真正的 SIGILL 将具有 ILL_ 值之一的 si_code(例如,ILL_ILLADR)。用户请求的 SIGILL 将具有 SI_ 值之一的 si_code(通常为 SI_USER)。

相关的 POSIX 值 如下:

[Kernel-generated]
ILL_ILLOPC  Illegal opcode.
ILL_ILLOPN  Illegal operand.
ILL_ILLADR  Illegal addressing mode.
ILL_ILLTRP  Illegal trap.
ILL_PRVOPC  Privileged opcode.
ILL_PRVREG  Privileged register.
ILL_COPROC  Coprocessor error.
ILL_BADSTK  Internal stack error.

[User-requested]
SI_USER     Signal sent by kill().
SI_QUEUE    Signal sent by the sigqueue().
SI_TIMER    Signal generated by expiration of a timer set by timer_settime().
SI_ASYNCIO  Signal generated by completion of an asynchronous I/O request.
SI_MESGQ    Signal generated by arrival of a message on an empty message queue.

例如,这个问题中的配方给了我ILL_ILLOPN,而kill(1)kill(2)给了我零(SI_USER)。
当然,您的实现可能会向POSIX列表中添加值。历史上,用户或进程生成的si_code值小于等于0,这仍然很常见。您的实现可能还有一个方便的宏来协助处理这个问题。例如,Linux提供了:
#define SI_FROMUSER(siptr)      ((siptr)->si_code <= 0)
#define SI_FROMKERNEL(siptr)    ((siptr)->si_code > 0)

实际上,由 kill 生成的信号的 si_code 应该是 SI_USER,通常为0,但在某些系统上可能是其他值。它将与所有 ILL_ 值都不同。 - Chris Dodd
@nos,是的,但是原帖作者的测试表明他不能仅依赖该字段。 - pilcrow
谢谢,你当然是正确的。我被手册中“以下值可以放置在 si_code 中以用于 SIGILL 信号”这句话搞糊涂了。 - Armali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接