使用 GDB 调试器调试程序的过程,其实就是借助 GDB 调试器来监控程序的执行流程,进而发现程序中导致异常或者 Bug 的代码。通过前面章节的学习,读者已经学会了如何启动 GDB 调试器,在此基础上,本节继续为大家讲解如何在 GDB 调试器中启动(运行)程序,以及启动程序过程中的一些注意事项。
根据不同场景的需要,GDB 调试器提供了多种方式来启动目标程序,其中最常用的就是 run 指令,其次为 start 指令。也就是说,run 和 start 指令都可以用来在 GDB 调试器中启动程序,它们之间的区别是:
- 默认情况下,
run指令会一直执行程序,直到执行结束。如果程序中手动设置有断点,则run指令会执行程序至第一个断点处; start指令会执行程序至 main() 主函数的起始位置,即在 main() 函数的第一行语句处停止执行(该行代码尚未执行)。
可以这样理解,使用 start 指令启动程序,完全等价于先在 main() 主函数起始位置设置一个断点,然后再使用 run 指令启动程序。另外,程序执行过程中使用 run 或者 start 指令,表示的是重新启动程序。
问一个问题,GDB 调试器启动后是否就可以直接使用 run 或者 start 指令了呢?答案当然是否定的。我们知道,启动 GDB 调试器的方式有多种,其中简单的方法就是直接使用 gdb 指令,例如:
[root@iZbp18vd1p2tytbwn5vgaqZ ~]# gdbGNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8Copyright (C) 2018 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>This is free software: you are free to change and redistribute it.There is NO WARRANTY, to the extent permitted by law.Type "show copying" and "show warranty" for details.This GDB was configured as "x86_64-redhat-linux-gnu".Type "show configuration" for configuration details.For bug reporting instructions, please see:<http://www.gnu.org/software/gdb/bugs/>.Find the GDB manual and other documentation resources online at:<http://www.gnu.org/software/gdb/documentation/>.For help, type "help".Type "apropos word" to search for commands related to "word".(gdb)
注意,使用此方式启动的 GDB 调试器,尚未指定要调试的目标程序,何谈使用 run 或者 start 指令呢?
不仅如此,在进行 run 或者 start 指令启动目标程序之前,还可能需要做一些必要的准备工作,大致包括以下几个方面:
- 如果启动 GDB 调试器时未指定要调试的目标程序,或者由于各种原因 GDB 调试器并为找到所指定的目标程序,这种情况下就需要再次手动指定;
- 有些 C 或者 C++ 程序的执行,需要接收一些参数(程序中用
argc和argv[]接收); - 目标程序在执行过程中,可能需要临时设置 PATH 环境变量;
- 默认情况下,GDB 调试器将启动时所在的目录作为工作目录,但很多情况下,该目录并不符合要求,需要在启动程序手动为 GDB 调试器指定工作目录。
- 默认情况下,GDB 调试器启动程序后,会接收键盘临时输入的数据,并将执行结果会打印在屏幕上。但 GDB 调试器允许对执行程序的输入和输出进行重定向,使其从文件或其它终端接收输入,或者将执行结果输出到文件或其它终端。
假设使用 GDB 调试器调试如下程序:
//存储路径为 /tmp/demo/main.c//其生成的可执行文件为 main.exe,位于同一路径下#include<stdio.h>int main(int argc,char* argv[]) {FILE * fp;if((fp = fopen(argv[1],"r")) == NULL){printf("file open fail");}else{printf("file open true");}return 0;}
要知道,命令行窗口打开时默认位于 ~ (表示当前用户的主目录)路径下,假设我们就位于此目录中使用 gdb 命令启动 GDB 调试器,则在执行 main.exe 之前,有以下几项操作要做:
首先,对于已启动的 GDB 调试器,我们可以先通过
l(小写的 L)指令验证其是否已找到指定的目标程序文件:
[root@bogon ~]# gdb -q <-- 使用 -q 选项,可以省略不必要的输出信息(gdb) lNo symbol table is loaded. Use the "file" command.
可以看到,对于找不到目标程序文件的 GDB 调试器,l指令的执行结果显示“无法加载符号表”。这种情况下,我们就必须手动为其指定要调试的目标程序,例如:
(gdb) file /tmp/demo/main.exeReading symbols from /tmp/demo/main.exe...(gdb) l1 #include<stdio.h>2 int main(int argc,char* argv[])3 {4 FILE * fp;5 if((fp = fopen(argv[1],"r")) == NULL){6 printf("file open fail");7 }8 else{9 printf("file open true");10 }(gdb)11 return 0;12 }(gdb)
可以看到,通过借助 file 命令,则无需重启 GDB 调试器也能指定要调试的目标程序文件。
除了 file 指令外,GDB 调试器还提供有其它的指定目标调试文件的指令,感兴趣的读者可千万 GDB 官网做详细了解,后续章节在用到时也会做详细的讲解。
通过分析 main.c 中程序的逻辑不难发现,要想其正确执行,必须在执行程序的同时给它传递一个目标文件的文件名。
总的来说,为 GDB 调试器指定的目标程序传递参数,常用的方法有 3 种:
启动 GDB 调试器时,可以在指定目标调试程序的同时,使用
--args选项指定需要传递给该程序的数据。仍以 main.exe 程序为例:
整个指令的意思是:启动 GDB 调试器调试 main.exe 程序,并为其传递 “a.txt” 这个字符串(其会被argv[]字符串数组接收)。[root@bogon demo]# gdb --args main.exe a.txt
GDB 调试器启动后,可以借助
set args命令指定目标调试程序启动所需要的数据。仍以 main.exe 为例:
该命令表示将 “a.txt” 传递给将要调试的目标程序。(gdb) set args a.txt
除此之外,还可以使用
run或者start启动目标程序时,指定其所需要的数据。例如:
以上 2 条命令都可以将 “a.txt” 传递给要调试的程序。(gdb) run a.txt(gdb) start a.txt
要知道,对于调试 /tmp/demo/ 路径下的 main.exe 文件,将其作为 GDB 调试器的工作目录,一定程度上可以提高我们的调试效率。反之,如果 GDB 调试器的工作目录和目标调试文件不在同一目录,则很多时候需要额外指明要操作文件的存储路径(例如第 1) 种情况中用 file 指令指明调试文件时就必须指明其存储位置)。
默认情况下,GDB 调试器的工作目录为启动时所使用的目录。例如在 ~ 路径下启动的 GDB 调试器,其工作目录就为 ~(当前用户的 home 目录)。当然,GDB 调试器提供有修改工作目录的指令,即 cd 指令。例如,将 GDB 调试器的工作目录修改为 /tmp/demo,则执行指令为:
(gdb) cd /tmp/demo
由此,GDB 调试器的工作目录就变成了 /tmp/demo。
某些场景中,目标调试程序的执行还需要临时修改 PATH 环境变量,此时就可以借助
path 指令,例如:
(gdb) path /temp/demoExecutable and object file path: /temp/demo:/usr/local/sbin:/usr/local/bin...
注意,此修改方式只是临时的,退出 GDB 调试后会失效。
默认情况下,GDB 调试的程序会接收
set args 等方式指定的参数,同时会将输出结果打印到屏幕上。而通过对输入、输出重定向,可以令调试程序接收指定文件或者终端提供的数据,也可以将执行结果输出到文件或者某个终端上。
例如,将 main.exe 文件的执行结果输出到 a.txt 文件中,执行如下命令:
(gdb) run > a.txt
由此,在 GDB 调试的工作目录下就会生成一个 a.txt 文件,其中存储的即为 main.exe 的执行结果。
总的来说,只有将调试程序所需的运行环境搭建好后,才能使用 run 或者 start 命令开始调试。如下是一个完整的实例,演示了 GDB 调试 mian.exe 之前所做的准备工作:
[root@bogon demo]# pwd <--显示当前工作路径/tmp/demo[root@bogon demo]# ls <-- 显示当前路径下的文件a.txt main.c main.exe[root@bogon demo]# cd ~ <-- 进入 home 目录[root@bogon ~]# gdb -q <-- 开启 GDB 调试器(gdb) cd /tmp/demo <-- 修改 GDB 调试器的工作目录Working directory /tmp/demo.(gdb) file main.exe <-- 指定要调试的目标文件Reading symbols from main.exe...(gdb) set args a.txt <-- 指定传递的数据(gdb) run <-- 运行程序Starting program: /tmp/demo/main.exe a.txtfile open true[Inferior 1 (process 43065) exited normally]
