想要熟练利用GDB进行程序调试,首先要了解什么是GDB。
1. 什么是GDB
GDB (the GNU Project Debugger) 是一个可以运行在大多数常见的UNIX架构、Windows、Mac OSX等系统上的跨平台调试器,允许我们查看另一个程序在运行过程中内部发生了什么——或者另一个程序崩溃时在做什么。
具体而言,GDB能做以下四种事情[1],以帮助我们定位运行中的Bug:
- 让程序开始运行,指定任何可能影响其行为的内容。
- 让程序在特定条件下停止运行。
- 检查程序停止运行时发生了什么。
- 在程序中改变一些变量或条件,修复当前的Bug使其正常执行,继续检查下一个。
根据官网介绍,GDB (9.1版本) 支持 Ada/Assembly/C/C++/D/Fortran/Go/Objective-C/OpenCL/Modula-2/Pascal/Rust 等编程语言的调试。
2. 调试原理
那么 GDB 是如何调试程序,其实现的原理是什么呢?为什么它可以控制程序执行、中断、访问内存甚至改变程序流程呢?
这就要从一个强大的系统调用 ptrace() 说起了。
2.1 系统调用 ptrace
简单来说,ptrace 系统调用提供了一种方法,让父进程可以观察和控制其它进程的执行,检查和改变其核心映像和寄存器。主要用来实现断点调试和系统调用跟踪。
GDB 的跟踪调试允许设置程序断点 break point,父进程通过 ptrace 接管子进程除了 SIGKILL 之外的所有信号。子进程(就是我们调试的程序)在发送 break point 或者单步调试时,会产生一个信号 SIGTRAP,被父进程(这里的GDB)捕获到,这时用户就可以通过 GDB 实时观察到当前子进程的状态。
2.2 三种调试方式
2.2.1 gdb executable_file
运行并调试一个新的进程。
首先通过fork()系统调用创建一个新进程,在新创建的子进程中调用 ptrace(PTRACE_TRACEME, 0, 0, 0),在子进程中通过execv()系统调用加载用户指定的可执行文件。
2.2.2 gdb executable_file corefile
如果程序发生了 core dump,出现段错误 (segment fault) 等,该命令可用于查看 coredump 文件信息,定位coredump产生原因、触发源。
2.2.3 gdb attach pid
调试一个已经运行的进程,如服务器进程等,同 gdb -p pid
首先需要确定待调试进程的进程 id,可利用 ps 指令获取 pid;然后运行 gdb attch pid,gdb 将对指定进程执行如下操作:ptrace (PTRACE_ATTACH, pid, 0, 0)
3. 信息显示
以 MacOS Mojave 10.14.6 系统为例
3.1 终端输入 gdb 进入调试器,一般启动时会显示如下信息
GNU gdb (GDB) 8.3 Copyright (C) 2019 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-apple-darwin18.5.0". 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)显示信息
如果不想显示这些信息,可以通过 -q 参数将其关掉
cv-MacBook-Pro:~ cv$ gdb -q (gdb)
3.2 在调试过程中,任何时候如果想查看 GDB 版本信息
(gdb) show version
GNU gdb (GDB) 8.3 Copyright (C) 2019 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-apple-darwin18.5.0". 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) show version
3.3 如果想查看 GDB 版权相关信息
(gdb) show copying
GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you --Type <RET> for more, q to quit, c to continue without paging-- have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as --Type <RET> for more, q to quit, c to continue without paging-- changed, so that their problems will not be attributed erroneously to authors of previous versions. ......(gdb) show copying
或者
(gdb) show warranty
15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ......(gdb) show warranty
3.4 当 gdb 输出信息较多时会暂停输出,并打印 “---Type <return> to continue, or q <return> to quit---” 这样的提示信息
如果想要让其中间不暂停,输出全部内容,设置
cv-MacBook-Pro:~ cv$ gdb (gdb) set pagination off
或者
cv-MacBook-Pro:~ cv$ gdb (gdb) set height 0
附录A
A.i 在大多 Linux 系统如 Ubuntu16.04 中 ptrace 系统调用原型如下
ptrace definition- enum __ptrace_request request 指示ptrace要执行的命令。
- pid_t pid 指示ptrace要跟踪的进程。
- void *addr 指示要监控的内存地址。
- void *data 存放读取出的或者要写入的数据。
request 的主要选项
- PTRACE_TRACEME 由子进程调用,表示本进程将被其父进程跟踪,交付给这个进程的所有信号,即使信号是忽略处理的(SIGKILL 除外),都会使其停止,父进程通过 wait() 获知这一情况
- PTRACE_ATTACH 指attach到一个指定的进程,使其成为当前进程跟踪的子进程,而子进程的行为等同于它进行了一次 PTRACE_TRACEME 操作。但要注意的是,虽然当前进程成为被跟踪进程的父进程,但是子进程使用 getppid() 得到的仍是其原始父进程的 pid。当在 GDB 中使用 attach 命令来跟踪一个指定进程/线程时,GDB 就自动成为该进程的父进程,而被跟踪的进程则使用了一次 PTRACE_TRACEME,GDB 也就顺理成章接管这个进程。
- PTRACE_CONT 继续运行之前停止的子进程。可同时向子进程交付指定的信号。
A.ii 在 MacOS Mojave 10.14.6 系统中通过 man ptrace 显示如下
#include <sys/types.h> #include <sys/ptrace.h> int ptrace(int request, pid_t pid, caddr_t addr, int data);
其中 request 有以下几个主要设置选项
- PT_TRACE_ME
此请求是被跟踪进程使用的两个请求之一,声明当前进程将被其父进程跟踪。所有其它参数都会被忽略。当一个进程使用了这个请求并调用execve(2)或其上构建的任何例程时,它将在执行新映像的第一条指令前停止。此外在可执行文件执行过程中,任何 setuid 或 setgid 位都会被忽略。
- PT_ATTACH/PT_ATTACHEXC
此请求允许进程获得对其它无关进程的控制并开始对其跟踪。不需要被跟踪进程的任何配合。此情况下,pid 指定将被跟踪的进程的进程 ID,其余两个参数被忽略。此请求要求目标进程与跟踪进程必须具有相同的真实UID,并且它不能执行 setuid 或 setgid 可执行文件。跟踪进程将感知到新跟踪的进程的停止,然后可以像一直跟踪一样控制它。注意此调用不同于先前的调用(PT_ATTACH),因为来自子调用的信号作为Mach异常传递给父调用。
- PT_STEP
单步执行被跟踪的进程。参数与传给 PT_CONTINUE 的相同。
- PT_CONTINUE
被跟踪的进程继续执行。addr指向即将被执行的进程的地址,或者为 1 表示从之前停止的地方开始继续执行。data提供在跟踪进程恢复执行时传递给跟踪进程的信号值,不发送信号则为 0。
- PT_DETACH
该请求与 PT_CONTINUE 基本相同,除了不允许在另一个备用位置继续执行,在它成功之后,被跟踪的进程将不再被跟踪,并且正常继续执行。
参考资料
[1] GDB: The GNU Project Debugger
[2] 100个gdb小技巧之信息显示
[3] GDB实现原理和使用范例
[4] 比较全面的gdb调试命令
[5] GDB使用记录