正如标题所说,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)

很容易注意到,对于SIGTRAPrip值指向下一条指令,而不是int3.

为什么指令指针在SIGTRAP上下文中调整为不重试相关int3

推荐答案

为什么指令指针在SIGTRAP上下文中被调整为不重试相关int3?

它不受内核的调整;硬件推送的异常返回地址是int条指令后的返回地址,包括int3条指令.

请记住,int3the normal case of int n(例如int 0x80)只是略有不同.int的设计类似于系统调用或远调用,因此(异常)返回地址是intafter.否则,除非内核在iret之前编辑了异常返回信息,否则int 0x80系统调用将永远重新运行.

那么为什么Linux的int3/int 3处理程序不将保存的RIP减少1呢?一方面,如果调试人员愿意,他们可以在软件中这样做,而保持内核简单更好.(对于does想要知道硬件推送的地址的软件来说,维护、效率和需求都很低.)

另一方面,1字节的固定偏移量并不总是正确的:2字节CD 03 int 3引发与1字节CC int3相同的异常.即使您想try ,x86机器代码也不会唯一地向后解码,因此模糊的机器代码可能会给出一个地址,而该地址不是执行的指令的实际开始.(尽管如果从那里解码,它将运行为int 3int3).e、 g.如果向后看,2字节rep int3add al, 0xf3/int3无法区分.

像GDB这样插入int3的软件将知道它插入了什么,并且需要知道目标机器的详细信息,因此可以处理偏移.

C++相关问答推荐

传递给空闲的无效地址0x71 db7 cb5e0:未分配值

使用sd-设备列举设备导致seg错误

初始化char数组-用于初始化数组的字符串是否除了存储数组的位置之外单独存储在内存中

C sscanf没有捕获第二个参数

为什么海湾合作委员会在共享对象中的. init_data的虚拟内存地址之前留出一个空白

从C函数调用asm函数时生成错误的BLX指令(STM32H753上的gcc)

两个连续的语句是否按顺序排列?

为什么C语言允许你使用var =(struct NAME){

我无法让LLDB正确运行我的可执行文件

将 struct 传递给函数

在循环中复制与删除相同条件代码的性能

如何计算打印二叉搜索树时每行所需的空间?

C中的FREE函数正在触发断点

从C文件中删除注释

在vfork()之后,链接器如何在不 destruct 父内存的情况下解析execve()?

-Wnonnull-Compare警告不是具有误导性吗?

OSDev--双缓冲重启系统

为什么会出现此错误?二进制表达式的操作数无效

哪些C++功能可以在外部C块中使用

";错误:寄存器的使用无效;当使用-masm=intel;在gcc中,但在AT&;T模式