如何用gdb调试程序呢?

本文详细指导如何使用GCC将程序编译为debug版本,解释了debuginfo和separatedebuginfo的概念,以及为何需要库的debuginfo。通过实例展示了GDB调试过程和处理Missingseparatedebuginfos的方法,包括使用debuginfo包和链接操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一般情况下调试有几种手段呢?单步执行可以一次执行一步;再者一次跳过一个函数;设置断点;监视变量的值等等。

给定示例程序:

#include<stdio.h> // line 1
// line 2
int main (int argc, char *argv[]) { // line 3
    int count; // line 4
// line 5
    for (count=0;count<10;count++) { // line 6
       printf("Hello %d\n", count); // line 7
    } // line 8
    return 0; // line 9
} // line 10
[user@localhost myctest]$ gdb ./example
GNU gdb (GDB) Fedora 8.2-3.fc29
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
......

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./example...(no debugging symbols found)...done.

可以看到没有调试信息的提示no debugging symbols found,所以程序在编译时要加上调试信息的debug版本。所以要将程序编译成 debug 版本。

如何使用 gcc 将程序编译成 debug 版本?

可以查看 gcc 的手册 man gcc 中的编译参数

Options for Debugging Your Program

  -g  Produce debugging information in the operating system's native format (stabs, COFF, XCOFF, or DWARF).
      GDB can work with this debugging information.

      On most systems that use stabs format, -g enables use of extra debugging information that only GDB can
      use; this extra information makes debugging work better in GDB but probably makes other debuggers
      crash or refuse to read the program.  If you want to control for certain whether to generate the extra
      information, use -gstabs+, -gstabs, -gxcoff+, -gxcoff, or -gvms (see below).

  -ggdb
      Produce debugging information for use by GDB.  This means to use the most expressive format available
      (DWARF, stabs, or the native format if neither of those are supported), including GDB extensions if at
      all possible.
... ... ... ...
  -gstabs
      Produce debugging information in stabs format (if that is supported), without GDB extensions.  This is
      the format used by DBX on most BSD systems.  On MIPS, Alpha and System V Release 4 systems this option
      produces stabs debugging output that is not understood by DBX.  On System V Release 4 systems this
      option requires the GNU assembler.

  -gstabs+
      Produce debugging information in stabs format (if that is supported), using GNU extensions understood
      only by the GNU debugger (GDB).  The use of these extensions is likely to make other debuggers crash
      or refuse to read the program.
... ... ... ...
-glevel
-ggdblevel
-gstabslevel
...
Request debugging information and also use level to specify how much information. The default level is 2.

Level 0 produces no debug information at all. Thus, -g0 negates -g.

Level 1 produces minimal information, enough for making backtraces in parts of the program that you don’t plan 
to debug. This includes descriptions of functions and external variables, and line number tables, but no information 
about local variables.

Level 3 includes extra information, such as all the macro definitions present in the program. Some debuggers support 
macro expansion when you use -g3.

If you use multiple -g options, with or without level numbers, the last such option is the one that is effective.
... ... ... ...

Options That Control Optimization

-Og
Optimize debugging experience. -Og should be the optimization level of choice for the standard edit-compile-debug cycle, 
offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience. It is a better 
choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0.

我们这里用默认的参数 -g

[user@localhost myctest]$ 
gcc example.c -o example
gcc example.c -o example.g -g

[user@localhost myctest]$ ls -l --human-readable example*
-rwxrwxr-x. 1 user user  19K Aug  9 00:22 example
-rw-rw-r--. 1 user user 3.6K Aug  6 18:42 example.c
-rwxrwxr-x. 1 user user  22K Aug  9 00:22 example.g

可以使用 readelf --debug-dump=aranges,str example.g 查看 debug 版本里的debug信息
然后运行 gdb ./example.g 就可以看到

Reading symbols from example.g...done.
(gdb) 
Reading symbols from example.g...done.
(gdb) break main
Breakpoint 1 at 0x401135: file example.c, line 6.
(gdb) run
Starting program: /home/user/works/myctest/example.c 
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.28-33.fc29.x86_64

Breakpoint 1, main (argc=1, argv=0x7fffffffd5e8) at example.c:6
6	    for (count=0;count<10;count++) {// line 6
(gdb) q

发现 Missing separate debuginfos 只需要按提示安装相应库的 debuginfo 包就行了。
sudo dnf debuginfo-install glibc-2.28-33.fc29.x86_64

常用命令

运行类命令
run – 运行程序。注意:比如C++ 程序,当需要在程序里用 cin 等接收数据时,可以放在文件里并用类似 run < data.txt 的方法进行传入
start – 相当于在主函数处设置断点然后运行 run 命令
next 单步执行并跳过函数
step 单步执行跳进函数
continue 继续运行

数据相关的命令
display 运行过程中显示变量的值
print 打印变量的值
set 设置变量值

断点相关的命令
break 设置断点
commands 设置遇到断点时所执行的命令

状态相关的命令
info breakpoints 显示断点信息

文件相关的命令
list 查看指定行或者函数附近的代码,毕竟字符界面不是多窗口显示,这个功能方便看源码

其它命令查看手册

示例
#include<stdio.h> // line 1
// line 2
int main (int argc, char *argv[]) { // line 3
    int count; // line 4
// line 5
    for (count=0;count<10;count++) { // line 6
       printf("Hello %d\n", count); // line 7
    } // line 8
    return 0; // line 9
} // line 10

gdb exmaple.g 或者 gdb --quiet example.g (--quiet 不显示额外的 gdb 版本信息)

Reading symbols from example.g...done.
(gdb) break main
Breakpoint 1 at 0x401135: file example.c, line 6.
(gdb) run
Starting program: /home/user/works/cpp/example.g 
warning: Loadable section ".note.gnu.property" outside of ELF segments

Breakpoint 1, main (argc=1, argv=0x7fffffffd5d8) at exmaple.c:6
6	    for (count=0; count<10; count++) {
(gdb) step
7	       printf("Print Number: %d\n", count); 
(gdb) display count
1: count = 0
(gdb) set count=6
(gdb) next
Print Number: 6
6	    for (count=0; count<10; count++) {
1: count = 6
(gdb) next
7	       printf("Print Number: %d\n", count); 
1: count = 7
(gdb) continue
Continuing.
Print Number: 7
Print Number: 8
Print Number: 9
[Inferior 1 (process 21899) exited normally]
(gdb) quit
处理 Missing separate debuginfos
什么是 separate debuginfo ?

将 debug 信息放在一个独立的文件中。如对于 gcc example.c -o example.g -g 所生成的 debug 版本的可执行程序 example.g 文件包含了 debug 所需要的信息。

objcopy --only-keep-debug example.g example.g.debug
strip -g example.g

这两条命令创建了只包含 debug 信息的 example.g.debug(不可执行) 和 去除了 debug 信息的 example.g,实现了 debug 信息和可执行程序文件的分离。
然后再将二者链接起来

objcopy --add-gnu-debuglink=example.g.debug example.g

这样可以通过 readelf -S example.g | grep debugreadelf --debug-dump example.g可以看到 example.g 里只有 .gnu_debuglink 而其它的 debug section 都在 example.g.debug 里。
同样可以看到 example.g 里 debug 相关的 section 里只有链接信息

[user@localhost myctest]$ readelf --debug-dump example.g 
Contents of the .gnu_debuglink section:

  Separate debug info file: example.g.debug
  CRC value: 0xefae614e

上面操作的相关文档可以查看 man objcopy, man strip DOC: Separate-Debug-Files

再看系统库的 glibc 的独立 debug 文件:
sudo dnf debuginfo-install glibc-2.28-39.fc29.x86_64 安装了 glibc-debuginfo-2.28-39.fc29.x86_64 glibc-debuginfo-common-2.28-39.fc29.x86_64。注意如果 glibc 与 glibc-debuginfo 的版本不一样会有 CRC mismatch 的 warning 警告

[user@localhost myctest]$ rpm --query --list glibc-debuginfo-2.28-39.fc29.x86_64 | grep libc-2.28.so.debug
/usr/lib/debug/lib64/libc-2.28.so.debug
[user@localhost myctest]$ readelf -S /usr/lib64/libc-2.28.so | grep debug
  [73] .gnu_debuglink    PROGBITS         0000000000000000  002a6df0
[user@localhost myctest]$ readelf -S /lib64/libc-2.28.so | grep debug
  [73] .gnu_debuglink    PROGBITS         0000000000000000  002a6df0
[user@localhost myctest]$ readelf --debug-dump=links /usr/lib64/libc-2.28.so 
Contents of the .gnu_debuglink section:

  Separate debug info file: libc-2.28.so.debug
  CRC value: 0x9bd5ece3

可以看到 glibc-debuginfo 里安装了独立的 debug 信息文件,并往库文件里添加了链接。
同时 debuginfo 包还往 /usr/src/debug/里放了源码文件

[user@localhost myctest]$ ls -l /usr/src/debug/
total 4
drwxr-xr-x. 60 root root 4096 Aug 10 18:36 glibc-2.28-110-g57922433fa

这些打包好的 debuginfo 包是由 /usr/lib/rpm/find-debuginfo.sh 产生的。

为什么需要库的 debuginfo ?

在调试过程中显示函数的调用栈。需要库的源文件和包含调试信息的 debug 文件。
调用栈示例如下:

Reading symbols from example.g...done.
(gdb) start
Temporary breakpoint 1 at 0x401135: file example.c, line 6.
Starting program: /home/codc/works/cpp/example.g 
warning: Loadable section ".note.gnu.property" outside of ELF segments

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffd5d8) at example.c:6
6       for (count=0; count<10; count++) {
(gdb) break printf
Breakpoint 2 at 0x7ffff7e32100: file printf.c, line 28.
(gdb) commands 2
Type commands for breakpoint(s) 2, one per line.
End with a line saying just "end".
>silent
>backtrace
>continue
>end
(gdb) continue
Continuing.

Breakpoint 2, __printf (format=0x402010 "Print Number: %d\n") at printf.c:28
28  {
#0  __printf (format=0x402010 "Print Number: %d\n") at printf.c:28
#1  0x0000000000401152 in main (argc=1, argv=0x7fffffffd5d8) at example.c:7
Print Number: 0

Breakpoint 2, __printf (format=0x402010 "Print Number: %d\n") at printf.c:28
28  {
#0  __printf (format=0x402010 "Print Number: %d\n") at printf.c:28
#1  0x0000000000401152 in main (argc=1, argv=0x7fffffffd5d8) at example.c:7
Print Number: 1
...

[1] https://stackoverflow.com/questions/12926839/about-gdb-and-crc-mismatch
[2] https://access.redhat.com/solutions/1506273
[3] https://unix.stackexchange.com/questions/148652/missing-separate-debuginfos/664754#664754
[4] https://fedoraproject.org/wiki/StackTraces
[5] https://stackoverflow.com/questions/4521015/how-to-pass-arguments-and-redirect-stdin-from-a-file-to-program-run-in-gdb
[6] https://stackoverflow.com/questions/10290898/gdb-input-files-and-such

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值