jump 命令基本用法是:
jump <location>
location 可以是程序的行号或者函数的地址,jump 会让程序执行流跳转到指定位置执行,当然其行为也是不可控制的,例如您跳过了某个对象的初始化代码,直接执行操作该对象的代码,那么可能会导致程序崩溃或其他意外行为。jump 命令可以简写成 j,但是不可以简写成 jmp,其使用有一个注意事项,即如果 jump 跳转到的位置后续没有断点,那么 GDB 会执行完跳转处的代码会继续执行。举个例子:
1 int somefunc()
2 {
3 //代码A
4 //代码B
5 //代码C
6 //代码D
7 //代码E
8 //代码F
9 }
假设我们的断点初始位置在行号 3 处(代码 A),这个时候我们使用 jump 6,那么程序会跳过代码 B 和 C 的执行,执行完代码 D( 跳转点),程序并不会停在代码 6 处,而是继续执行后续代码,因此如果我们想查看执行跳转处的代码后的结果,需要在行号 6、7 或 8 处设置断点。
jump 命令除了跳过一些代码的执行外,还有一个妙用就是可以执行一些我们想要执行的代码,而这些代码在正常的逻辑下可能并不会执行(当然可能也因此会产生一些意外的结果,这需要读者自行斟酌使用)。举个例子,假设现在有如下代码:
1 #include <stdio.h>
2 int main()
3 {
4 int a = 0;
5 if (a != 0)
6 {
7 printf("if condition\n");
8 }
9 else
10 {
11 printf("else condition\n");
12 }
13
14 return 0;
15 }
我们在行号 4 、14 处设置一个断点,当触发行号 4 处的断点后,正常情况下程序执行流会走 else 分支,我们可以使用 jump 7 强行让程序执行 if 分支,接着 GDB 会因触发行号 14 处的断点而停下来,此时我们接着执行 jump 11,程序会将 else 分支中的代码重新执行一遍。整个操作过程如下:
[root@localhost testcore]# gdb test
Reading symbols from /root/testcore/test...done.
(gdb) b main
Breakpoint 1 at 0x400545: file main.cpp, line 4.
(gdb) b 14
Breakpoint 2 at 0x400568: file main.cpp, line 14.
(gdb) r
Starting program: /root/testcore/test
Breakpoint 1, main () at main.cpp:4
4 int a = 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7.x86_64 libgcc-4.8.5-36.el7.x86_64 libstdc++-4.8.5-36.el7.x86_64
(gdb) jump 7
Continuing at 0x400552.
if condition
Breakpoint 2, main () at main.cpp:14
14 return 0;
(gdb) jump 11
Continuing at 0x40055e.
else condition
Breakpoint 2, main () at main.cpp:14
14 return 0;
(gdb) c
Continuing.
[Inferior 1 (process 13349) exited normally]
(gdb)
redis-server 在入口函数 main 处调用 initServer() ,我们使用 “b initServer” 、“b 2025”、“b 2027”在这个函数入口处、2025 行、2027 行增加三个断点,然后使用 run 命令重新运行一下程序,触发第一个断点后,继续输入 c 命令继续运行,然后触发 2025 行处的断点,接着输入 jmp 2027 :
(gdb) b 2025
Breakpoint 5 at 0x42c8e7: file server.c, line 2025.
(gdb) b 2027
Breakpoint 6 at 0x42c8f8: file server.c, line 2027.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) n
Program not restarted.
(gdb) b initServer
Note: breakpoint 3 also set at pc 0x42c8b0.
Breakpoint 7 at 0x42c8b0: file server.c, line 2013.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/redis-5.0.3/src/redis-server
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 1, main (argc=1, argv=0x7fffffffe4e8) at server.c:4003
4003 int main(int argc, char **argv) {
(gdb) c
Continuing.
13374:C 14 Jan 2019 15:12:16.571 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
13374:C 14 Jan 2019 15:12:16.571 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=13374, just started
13374:C 14 Jan 2019 15:12:16.571 # Warning: no config file specified, using the default config. In order to specify a config file use /root/redis-5.0.3/src/redis-server /path/to/redis.conf
Breakpoint 3, initServer () at server.c:2013
2013 void initServer(void) {
(gdb) c
Continuing.
Breakpoint 5, initServer () at server.c:2025
2025 server.hz = server.config_hz;
(gdb) jump 2027
Continuing at 0x42c8f8.
Breakpoint 6, initServer () at server.c:2027
2027 server.current_client = NULL;
(gdb)
程序将 2026 行的代码跳过了,2026 行处的代码是获取当前进程 id:
2026 server.pid = getpid();
由于这一行被跳过了,所以 server.pid 的值应该是一个无效的值,我们可以使用 print 命令将这个值打印出来看一下:
(gdb) p server.pid
$3 = 0
结果确实是 0 这个我们初始化的无效值。
本质上,jump 命令的作用类似于在 Visual Studio 中调试时,拖鼠标将程序从一个执行处拖到另外一个执行处。