GNU Debugger (GDB)

我的博客

原文链接

GNU 调试器 (GDB) 是一个可移植调试器,可以运行在一些类 UNIX 系统上,并且支持多种编程语言,包括 Ada,C,C++,Objective-C,Free Pascal,Fortran,Go 等。

历史

GDB 是由 Richard Stallman 在 GNU Emacs 稳定之后,于 1986 年写就,并做为他 GNU 系统的一部分。GDB 是一个开源软件,并在 GNU General Public License 证书下发布。它基于 DBX 调试器模型,DBX 来自于伯克利 UNIX

自 1990 年到 1993 年,由 John Gilmore 维护。现在它由 GDB Steering Committee 团队维护。

技术细节

特性

GDB 提供对计算机程序执行的跟踪与修改的扩展工具集。用户可以监视并修改程序内部的变量,甚至可以单独调用程序的函数。

GDB 支持目标处理器 (截至 2003) 包括 AlphaARMAVRH8/300Altera Nios/Nios IISystem/370X86X86-64IA-64Motorola 68000MIPSPA-RISCPowerPCSuperHSPARCVAX,以及一些鲜有人知的目标处理器,包括 A29KARCETRAX CRISD10VD30VFR-30FR-VIntel i96068HC11Motorola 88000MCOREMN10200MN10300NS32KStormy16Z8000,还有一些更少见的 M32RV850 等。

GDB 现在依旧在保持更新。7.0 版本支持对 Python 脚本的支持,7.8 版本支持 GNU Guile 脚本。自 7.0 版本,支持向后调试,允许调试会话向后退回,恢复崩溃程序以查看发生了什么。

远程调试

GDB 通常在调试嵌入式系统时支持远程调试。远程操作是在 GDB 运行在一台设备上,而调试的程序运行在另一个设备上。GDB 可以通过穿行设备或 TCP/IP 与远程设备以某种协议沟通。远程程序可以与 GDB 提供的合适的文件链接,这个文件提供目标侧的通讯协议。另外 gdbserver 可以用来进行远程调试而不需要对它进行修改。

相同的模式被 KGDB 使用来使用 gdb 进行对运行中的 Linux 内核进行源码级调试。使用 KGDB,内核开发者可以像调试应用程序一样调试内核。可以将断点放在内核源码中,执行代码,观察变量。在那些具有硬件调试寄存器的架构中,可以设置 watchpoint,在指定的内存地址被执行或被访问时,触发中断。KDBG 需要一个额外的设备,使用串行线缆或者以太网连接到待调试的设备上。

图形用户界面

调试器不包含图形用户界面,默认为命令行接口,虽然它确实有一个文本用户接口。为了便于使用,构建了一下前端,比如 UltraGDBXxgdbData Display DebuggerNemiverKDbgXcode debuggerGDBtk/Insight 以及 HP Wildebeest Debugger GUI (WDB GUI)。一些集成开发环境 (IDE),比如 CodeliteCode::BlocksDev-C++GeanyGNAT Programming StudioKDevelopQt CreatorLazarusMonoDevelopEclipseNetBeans 以及 Visual Studio 可以添加 GDB 做为它们的组件。

一些调试工具设计配合 GDB 工作,比如内存泄漏检测器。

内部

GDB 使用称作 ptrace 的系统调用 (process trace 的简写) 来观察控制另一个程序的执行情况、确定并修改处理器的内存以及寄存器。下面的 gdb 命令以及对应的 ptrace 调用列出如下:

  • (gdb) start: PTRACE_TRACEME,令父进程做为一个跟踪者
  • (gdb) attach PID: PTRACE_ATTACH,连接到一个运行的进程
  • (gdb) stop: kill(child_pid, SIGSTOP)PTRACE_INTERRUPT
  • (gdb) continue: PTRACE_CONT
  • (gdb) info registers: PTRACE_GET(FPREGS(ET) 以及 PTRACE_SET(FP)REGS(ET)
  • (gdb) x: PTRACE_PEEKTEXT 以及 PTRACE_POKETEXT

通过将指定内存地址的指令替换为另一个特殊指令实现断点。执行断点指令会造成 SIGTRAP

命令用例

命令 描述
gdb program 调试 program 程序 (由 shell 启动)
run -v 使用指定参数,开始程序的运行
bt 回溯 (防止程序崩溃)
info registers 打印所有寄存器信息
disas $pc-32, $pc+32 反汇编

案例

考虑下面的 C 源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

size_t foo_len(const char *s)
{
    return strlen(s);
}

int main(int argc, char *argv[])
{
    const char *a = NULL;
    printf("size of a = %lu\r\n", foo_len(a));
    
    exit(0);
}

使用 GCCLinux 上编译这段程序,上面的代码必须使用 -g 选项进行编译,以此包含合适的调试信息,这样就能够使用 GDB 进行调试了,假设上面的源码被保存为 example.c 源文件,可以使用下面的命令进行编译:

$ gcc example.c -Og -g -o example

现在二进制可以运行了:

$ ./example
Segmentation fault

上面的程序在运行时产生了段错误的报错,可以使用 GDB 排错。

$ gdb ./example
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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-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"...
Reading symbols from ./example...
(gdb) run
Starting program: /home/arv/0_test/example

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555179 in foo_len (s=0x0) at example.c:7
7               return strlen(s);
(gdb) print s
$1 = 0x0

问题在源码第 7 行调用函数 strlen 时出现(因为它的参数 sNULL)。gdb 运行的实际情况可能与上面的结果不同,取决于 strlen 的实现方式(是否内联),输出可能如下:

$ gdb ./example
GNU gdb (Raspbian 10.1-1.7) 10.1.90.20210103-git
Copyright (C) 2021 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 "arm-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://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"...
Reading symbols from ./example...
(gdb) run
Starting program: /home/arv/test/example

Program received signal SIGSEGV, Segmentation fault.
0xb6fba1dc in strlen () from /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
(gdb) bt
#0  0xb6fba1dc in strlen () from /usr/lib/arm-linux-gnueabihf/libarmmem-v7l.so
#1  0x00010470 in foo_len (s=s@entry=0x0) at example.c:7
#2  0x00010480 in main (argc=<optimized out>, argv=<optimized out>) at example.c:14

为了修复这个问题,在函数 main 中的变量 a 必须给到一个合法的字符串,下面是对源码的修复:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

size_t foo_len( const char *s )
{
  return strlen(s);
}

int main( int argc, char *argv[] )
{
  const char *a = "This is a test string";

  printf( "size of a = %lu\n", foo_len(a) );

  exit( 0 );
}

重新编译并在 GDB 中运行:

$ gdb ./example
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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-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"...
Reading symbols from ./example...
(gdb) run
Starting program: /home/arv/0_test/example
size of a = 21
[Inferior 1 (process 2638) exited normally]

gdbprintf 的打印结果打印到屏幕上,并告知用户程序正常退出。

posted @ 2022-11-27 22:32  ArvinDu  阅读(361)  评论(0编辑  收藏  举报