正如标题所说,ucontext_t
中的-rip
并不是指int3
提高了SIGTRAP
.相反,它指向下一条指令.
这与我的(天真的)期望不同,即在信号处理程序返回时将重试每个出错的指令(如果上下文没有更改,并且进程在处理程序中没有显式终止).
另一方面,当获得SIGILL
个上下文点时,则指向错误的指令.同样关于ARM和Aarch64-SIGTRAP
的上下文也指向了相关的bkpt #0
/bpt #0
.
测试程序:
/* sigtest.c */
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
extern void do_int3(void);
extern void do_ud2(void);
void sighandler(int signo, siginfo_t* info, void* context) {
struct ucontext_t* uctx = context;
/* Yes, printf() here is bad. I promise to never ever do this in real programs */
printf("Got signal %d:\n"
"\tsi_addr: %p\n"
"\tcontext RIP: %p\n",
signo,
info->si_addr,
(void*)uctx->uc_mcontext.gregs[REG_RIP]);
}
int main(int argc, char** argv) {
struct sigaction sa = {};
sa.sa_flags = SA_SIGINFO | SA_ONESHOT;
sa.sa_sigaction = sighandler;
sigemptyset(&sa.sa_mask);
sigaction(SIGTRAP, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
void (*fn)(void) = 0;
if (argv[1][0] == 't') {
fn = do_int3;
}
if (argv[1][0] == 'u') {
fn = do_ud2;
}
printf("call function at %p\n", fn);
fn();
printf("call returned\n");
}
; do_int3.S
.text
.globl do_int3
.type do_int3, @function
do_int3:
int3
ret
.size do_int3, .-do_int3
; do_ud2.S
.text
.globl do_ud2
.type do_ud2, @function
do_ud2:
ud2
ret
.size do_ud2, .-do_ud2
编译并运行:
$ cc sigtest.c do_int3.S do_ud2.S
$ ./a.out t
call function at 0x5620b87c6908
Got signal 5:
si_addr: (nil)
context RIP: 0x5620b87c6909
call returned
$ ./a.out u
call function at 0x557d1bc1590a
Got signal 4:
si_addr: 0x557d1bc1590a
context RIP: 0x557d1bc1590a
Illegal instruction (core dumped)
很容易注意到,对于SIGTRAP
,rip
值指向下一条指令,而不是int3
.
为什么指令指针在SIGTRAP
上下文中调整为不重试相关int3
?