GDB调试系列之了解GDB

2020-03-01

想要熟练利用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使用记录