gdb调试信息分离

·

调试信息分离

GDB的调试信息分离机制

GDB 允许程序的调试信息和可执行文件分离。当可执行程序出现问题时,GDB 会自动查找和加载调试信息。

  • 这种机制的实现原理是,GDB 通过两种方式来指定独立调试信息文件:
  • 一种是可执行文件包含一个调试链接,它会指定独立调试信息文件的名称;
  • 另一种是可执行文件包含一个构建标识符,它是一个唯一的位串,在相应的调试信息文件中也包含这个位串?

调试链接详解

  • 调试链接包含两个关键信息:
  • 调试信息文件的名称和 CRC 校验和。 调试信息文件的名称通常为executable.debug,其中executable是相应的可执行文件的名称,不带有前导的目录名,方便查找。 CRC 校验和的目的是为了验证可执行程序和调试文件是否匹配。
  • 在生成调试链接时,会将调试信息文件的名称和 CRC 校验和添加到可执行文件中
  • 当 GDB 调试时,会根据这些信息来查找和加载调试信息。
    • 例如,在编译生成release with debug info时,它包含调试信息,然后把它进行调试信息分离,生成一个可执行程序(用a代替)和调试信息文件(用b代替)。
    • 接下来把调试链接做出来,就是在可执行程序a当中,加上一个链接,这个链接信息包含调试信息文件b的名字和 CRC 校验和 。

以 Redis 为例的调试流程

编译带调试信息的可执行文件

在编译 Redis 时,我们可以使用-O2或-O3优化选项来优化代码,提高运行效率,同时使用-g选项添加调试信息,-DENDEBUG用于禁用断言(在 Release 版本中通常会禁用断言以提高性能)。

make CFLAGS="-O2 -g -DENDEBUG"

这样就会生成一个包含调试信息的 Redis 可执行程序,虽然文件大小可能会有所增加,但为后续的调试提供了便利。

转存调试信息

我们使用objcopy工具将调试信息转存到一个新的文件中。命令如下:

objcopy --only-keep-debug ./src/redis-server redis-server.debug

这条命令会把./src/redis-server中的调试信息提取出来,保存到redis-server.debug文件中。

此时,redis-server.debug装满调试信息,里面包含了变量名、函数名、行号等重要信息。

去除可执行程序调试信息

为了得到一个 “干净” 的、不包含调试信息的可执行程序,我们使用strip命令:

strip ./src/redis-server -o redis-server

经过这一步,redis-server就变成了一个体积更小、运行效率更高的裸可执行程序, 但缺少了调试所需的信息。

添加调试链接

最后,我们要为这个裸可执行程序添加调试链接,让它能够找到对应的调试信息文件。使用objcopy命令:

objcopy --add-gnu-debuglink=redis-server.debug redis-server

这条命令会在redis-server中添加一个调试链接,链接到redis-server.debug文件。

此时,redis-server虽然不包含调试信息,但它知道从哪里获取调试信息,为后续的调试做好了准备。

用 GDB 调试

现在,我们就可以使用 GDB 来调试这个带有调试链接的可执行程序了。

启动 GDB 并加载redis-server:

gdb redis-server

GDB 会自动根据调试链接找到redis-server.debug文件,并加载其中的调试信息。

在 GDB 中,我们可以使用各种调试命令,如设置断点(break)、查看变量值(print)、单步执行(step)等,从而定位和解决程序中的问题。例如,我们可以在某个函数处设置断点,查看程序执行到该点时变量的值,分析程序的执行逻辑是否正确。

参考

gdb调试