如何单步执行 gdb 中的 longjmp

How to step through a longjmp in gdb

我正在尝试修复其他人编写的代码中的错误,并且我正在尝试在 gdb 中单步执行它以弄清楚发生了什么。但是我点击的其中一行是对 longjmp() 的调用,在该行点击 "next" 之后,gdb 继续正常执行,而不是在下一个正在执行的源代码行中断。如果我在 longjmp() 行上尝试 "step" ,则会发生类似的继续。是否有任何 gdb 命令可用于在 longjmp() 之后执行的下一个源代码行中断?

您需要在 setjmp 的非零 return 代码之后的行设置断点。

例如:

#include <stdio.h>
#include <setjmp.h>

jmp_buf jb;

void f1()
{
    printf("jumping\n");
    longjmp(jb, 1);
    printf("what???\n");
}

int main()
{
    if (!setjmp(jb)) {
        printf("calling f1\n");
        f1();
    } else {
        printf("jumped!!\n");    // line 19
    }
    return 0;
}

调用longjmp后,下一行要执行的是第19行(见注释)。因此,在这种情况下 运行 break 19 在 gdb 提示符下,您将在调用 longjmp.

后停在该行

gdb 输出:

(gdb) break 19
Breakpoint 2 at 0x40056d: file /tmp/x1.c, line 19.
(gdb) start
Temporary breakpoint 3 at 0x400549: file /tmp/x1.c, line 15.
Starting program: /tmp/x1
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000

Temporary breakpoint 3, main () at /tmp/x1.c:15
15          if (!setjmp(jb)) {
(gdb) step
16              printf("calling f1\n");
(gdb)
calling f1
17              f1();
(gdb)
f1 () at /tmp/x1.c:8
8           printf("jumping\n");
(gdb)
jumping
9           longjmp(jb, 1);
(gdb)

Breakpoint 2, main () at /tmp/x1.c:19
19              printf("jumped!!\n");
(gdb)
jumped!!
21          return 0;
(gdb)
22      }
(gdb)
0x0000003fa441d9f4 in __libc_start_main () from /lib64/libc.so.6
(gdb)

Is there any gdb command I can use to break on the next source line being executed after longjmp()?

据我所知,没有。您将需要跟踪 jmp_buf 的来源,备份调用堆栈以找到填充它的 setjmp()。如果源代码中的 setjmp() 调用很少,您可以考虑在每个调用之后设置一个断点。否则,您可能只是单步执行程序,在每个 setjmp() 通过后设置一个断点。

另请注意,当达到相应的 longjmp() 时,您确实应该在调用堆栈中的一个函数中找到相关的 setjmp(),否则 jmp_buf 无效。

您可以使用 bt 命令获取调用堆栈,并使用 frame 命令切换和检查堆栈中的任何帧。

值得注意的是,gdb 的行为取决于系统。

On Linux,next over a longjmp or throw 大致如您所料:如果非本地跳跃的目标在或以上当前帧,执行将停在那里。

这是在 longjmpthrow 的实现中使用调试挂钩实现的。对于 throw,这是通过辅助函数(旧方法)和所谓的 SystemTap 探针(又名 "sdt probes")完成的。对于 longjmp 这仅通过 glibc 中的 sdt 探测器完成。

为此,必须将探测支持编译到相关库中。您可以使用 readelf -n 查看。至少 Fedora 做到了这一点。

理论上可以在 gdb 中为其他平台实现对 longjmp 的支持。然而,它可能并不平凡。

您可以尝试解码 jmp_buf,如其他评论中所述。但是请注意,在某些系统上,例如 Linux,出于安全原因,jmp_buf 中的目标 PC 是经过编码的。所以你必须弄清楚如何解码它。

另一种解决方法是单步执行 longjmp 本身。在那里设置一个断点,比如 break longjmp;使用 disassemble 查看程序集;然后 si 直到您逐步完成将执行转移到目标的指令。