如何单步执行 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
大致如您所料:如果非本地跳跃的目标在或以上当前帧,执行将停在那里。
这是在 longjmp
和 throw
的实现中使用调试挂钩实现的。对于 throw
,这是通过辅助函数(旧方法)和所谓的 SystemTap 探针(又名 "sdt probes")完成的。对于 longjmp
这仅通过 glibc 中的 sdt 探测器完成。
为此,必须将探测支持编译到相关库中。您可以使用 readelf -n
查看。至少 Fedora 做到了这一点。
理论上可以在 gdb 中为其他平台实现对 longjmp
的支持。然而,它可能并不平凡。
您可以尝试解码 jmp_buf
,如其他评论中所述。但是请注意,在某些系统上,例如 Linux,出于安全原因,jmp_buf
中的目标 PC 是经过编码的。所以你必须弄清楚如何解码它。
另一种解决方法是单步执行 longjmp
本身。在那里设置一个断点,比如 break longjmp
;使用 disassemble
查看程序集;然后 si
直到您逐步完成将执行转移到目标的指令。
我正在尝试修复其他人编写的代码中的错误,并且我正在尝试在 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
大致如您所料:如果非本地跳跃的目标在或以上当前帧,执行将停在那里。
这是在 longjmp
和 throw
的实现中使用调试挂钩实现的。对于 throw
,这是通过辅助函数(旧方法)和所谓的 SystemTap 探针(又名 "sdt probes")完成的。对于 longjmp
这仅通过 glibc 中的 sdt 探测器完成。
为此,必须将探测支持编译到相关库中。您可以使用 readelf -n
查看。至少 Fedora 做到了这一点。
理论上可以在 gdb 中为其他平台实现对 longjmp
的支持。然而,它可能并不平凡。
您可以尝试解码 jmp_buf
,如其他评论中所述。但是请注意,在某些系统上,例如 Linux,出于安全原因,jmp_buf
中的目标 PC 是经过编码的。所以你必须弄清楚如何解码它。
另一种解决方法是单步执行 longjmp
本身。在那里设置一个断点,比如 break longjmp
;使用 disassemble
查看程序集;然后 si
直到您逐步完成将执行转移到目标的指令。