gdb调试单独的debuginfo文件

gdb调试单独的debug文件

2023.11.26

背景

Debug和Release区别

实际上,Debug 和 Release 并没有本质的界限,人为的区别,没有特殊的规定。他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。约定俗成的区别是:

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。约定俗成的是release版本不带调试信息。

不同的软件有自己不同的选择。有些软件会在release版本和debug版本之外,根据自己的需要,出别的版本,如QT分为debug、release、profile版本:

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。

Release 称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。

profile则是在这两种之中取一个平衡,兼顾性能和调试, 可以类似的看做是性能更优但是又方便调试的版本。

没有对错,也没有是否最优,看自己的目的,根据自己的目的进行取舍,平衡利弊后,使用一个最优的做法。比如有些软件可能为了在发布后调试方便,在添加调试信息的同时,也会添加优化选项进行优化,从而求得一个平衡。有些软件考虑发布时携带调试信息会让发布包变得非常大,不利于网络分发,就会在发布时去掉调试信息。

发布了Release版本后,出问题时定位问题的常用做法

Release版本确实比debug版本在运行速度及体积大小上有一定的优势,不过如果发布后的程序出现问题,对于定位问题的人员来说,就比较痛苦了,毕竟没有调试信息很不方便。为了解决这个问题,linux下的程序一般会有独立的debuginfo包,这个debuginfo包是和release包一一对应的,当release版本的程序出现问题时,将debuginfo包安装后,即可像debug版本一样进行调试。如ls命令,在gdb的时候,如果没有调试信息,会提示进行安装,我的操作系统是fedora,有如下提示:

[zy@fedora debuginfo]$ gdb /usr/bin/ls
GNU gdb (Fedora Linux) 13.2-10.fc39
Reading symbols from /usr/bin/ls...
This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Reading symbols from .gnu_debugdata for /usr/bin/ls...
(No debugging symbols found in .gnu_debugdata for /usr/bin/ls)
Missing separate debuginfos, use: dnf debuginfo-install coreutils-9.3-4.fc39.x86_64
(gdb) quit

可以看到,gdb提示使用dnf debuginfo-install coreutils-9.3-4.fc39.x86_64命令安装debuginfo包。安装完后再gdb,就可以看到已经读取到符号信息了,如下所示:

[root@fedora debug]# dnf debuginfo-install coreutils
[zy@fedora debuginfo]$ gdb --args /usr/bin/ls .
GNU gdb (Fedora Linux) 13.2-10.fc39
Reading symbols from /usr/bin/ls...
Reading symbols from /usr/lib/debug/usr/bin/ls-9.3-4.fc39.x86_64.debug...[zy@fedora debuginfo]$ gdb --args /usr/bin/ls .
GNU gdb (Fedora Linux) 13.2-10.fc39

如何制作单独的debuginfo文件

linux下可以通过objcopy命令分离出单独的debuginfo包,比如一个a.out程序,可以通过如下命令得到deuginfo包

objcopy --only-keep-debug ./a.out a.out.debug
objcopy --strip-debug ./a.out
objcopy --add-gnu-debuglink=a.out.debug ./a.out

objcopy的–only-keep-debug选项,将debug信息分离到一个单独的文件中。–strip-debug选项将debug信息从a.out中删除。–add-gnu-debuglink选项将a.out和前面生成的a.out.debug文件关联起来,这一步很重要,如果没有这一步,则在gdb调试的时候会找不到调试信息。

这只是其中一种方法,还有一种方法是在编译时生成build-id,将build-id信息放到编译后的文件中。本文不介绍此种方式。

gdb调试单独debuginfo文件的原理介绍

先看一个小例子,使用的gcc版本及gdb版本如下:

[zy@fedora debuginfo]$ gcc --version
gcc (GCC) 13.2.1 20231011 (Red Hat 13.2.1-4)
Copyright © 2023 Free Software Foundation, Inc.
[zy@fedora debuginfo]$ gdb --version
GNU gdb (Fedora Linux) 13.2-10.fc39
Copyright (C) 2023 Free Software Foundation, Inc.

以下是代码结构及具体的代码内容:

[zy@fedora gdb_test]$ ls
liba.c  liba.h  main.c
[zy@fedora gdb_test]$ cat liba.c 
#include <stdio.h>
#include <stdlib.h>

int liba_func(int a, int b)
{
    int sum = 0;

    sum = a + b;
    printf("%d + %d = %d.\n", a, b, sum);

    return 0;
}
[zy@fedora gdb_test]$ cat liba.h
#ifndef __LIBA_FUNC__
#define __LIBA_FUNC__
int liba_func(int a, int b);
#endif
[zy@fedora gdb_test]$ cat main.c 
#include <stdio.h>
#include <stdlib.h>
#include "liba.h"

int main()
{
    printf("hello, this is main.\n");
    liba_func(1, 2);
    return 0;
}

[zy@fedora gdb_test]$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.

如何添加调试信息到目标文件

当不加任何编译参数,直接gdb运行的时候,发现设置断点后,看不到调试信息,如下所示:

[zy@fedora gdb_test]$  gcc liba.c -fpic -shared  -o liba.so
[zy@fedora gdb_test]$ gcc main.c -L. -I. -la  -o test
[zy@fedora gdb_test]$ gdb ./test 
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./test)
(gdb) b main
Breakpoint 1 at 0x40113a
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, 0x000000000040113a in main ()
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function main,
which has no line number information.
main_sum = 90.

Breakpoint 2, 0x00007ffff7fbd10d in liba_func () from liba.so
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.

而加了-g选项后,再用gdb调试的时候,可以看到具体的调试信息,如下所示:

[zy@fedora gdb_test]$  gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ gcc main.c -L. -I. -la -g  -o test
[zy@fedora gdb_test]$ gdb ./test
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Breakpoint 1, main () at main.c:7
7        int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        int main_b = 5;
(gdb) c
Continuing.
main_sum = 90.

Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11        a = a * a;

和看不到调试信息的编译选项相比,多加了-g编译选项,查看编译后的文件,发现多了很多debug相关的segment,如下所示:

[zy@fedora gdb_test]$  gcc liba.c -fpic -shared  -o liba.so
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[zy@fedora gdb_test]$  gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
  [26] .debug_aranges    PROGBITS         0000000000000000  00003160
  [27] .debug_info       PROGBITS         0000000000000000  00003190
  [28] .debug_abbrev     PROGBITS         0000000000000000  00003273
  [29] .debug_line       PROGBITS         0000000000000000  0000330f
  [30] .debug_str        PROGBITS         0000000000000000  0000337a
  [31] .debug_line_str   PROGBITS         0000000000000000  0000342c

是否包含调试信息,关键在于编译时是否添加-g选项。添加了-g选项后,编译出来的文件中带有很多debug相关的段。可以将这些debug信息进行清除,如下所示:

[zy@fedora gdb_test]$  gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ file liba.so 
liba.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=cc2b549427fb52d590a07ad72efec99085b9a877, with debug_info, not stripped
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
  [26] .debug_aranges    PROGBITS         0000000000000000  00003160
  [27] .debug_info       PROGBITS         0000000000000000  00003190
  [28] .debug_abbrev     PROGBITS         0000000000000000  00003273
  [29] .debug_line       PROGBITS         0000000000000000  0000330f
  [30] .debug_str        PROGBITS         0000000000000000  0000337a
  [31] .debug_line_str   PROGBITS         0000000000000000  0000342c
[zy@fedora gdb_test]$ readelf -S liba.so |grep sym
  [ 4] .dynsym           DYNSYM           0000000000000328  00000328
  [32] .symtab           SYMTAB           0000000000000000  00003490
[zy@fedora gdb_test]$ strip -s liba.so 
[zy@fedora gdb_test]$ file liba.so 
liba.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=cc2b549427fb52d590a07ad72efec99085b9a877, stripped
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[zy@fedora gdb_test]$ readelf -S liba.so |grep sym
  [ 4] .dynsym           DYNSYM           0000000000000328  00000328

清除后,在编译后的文件中就看不到debug相关的段了。文件大小也比之前有所减少,如下所示:

[zy@fedora gdb_test]$  gcc liba.c -fpic -shared  -o liba.so
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 15800 11月15日 22:35 liba.so
[zy@fedora gdb_test]$  gcc liba.c -fpic -shared -g  -o liba.so
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 17080 11月15日 22:35 liba.so
[zy@fedora gdb_test]$ strip -s liba.so 
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 14392 11月15日 22:35 liba.so

使用objcopy命令分离出单独的debuginfo文件

前面已经介绍过,使用objcopy命令可以将debug信息分离到单独的文件中,如下所示:

[zy@fedora gdb_test]$ gcc main.c -L. -I. -la -g -o test
[zy@fedora gdb_test]$ ls
liba.c  liba.h  liba.so  main.c  test
[zy@fedora gdb_test]$ objcopy --only-keep-debug ./liba.so liba.so.debug
[zy@fedora gdb_test]$ objcopy --strip-all liba.so
liba.so        liba.so.debug  
[zy@fedora gdb_test]$ objcopy --strip-all liba.so
[zy@fedora gdb_test]$ ls
liba.c  liba.h  liba.so  liba.so.debug  main.c  test

如果不使用objcopy --add-gnu-debuglink=liba.so.debug liba.so命令将分离出的debuginfo文件和原始程序关联起来,在使用gdb调试的时候,发现无法看到调试信息,如下所示:

[zy@fedora gdb_test]$ gdb ./test
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./test)
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 2, 0x00007ffff7fbd10d in liba_func () from liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function

使用了objcopy --add-gnu-debuglink命令之后,可以看到具体的调试信息了,如下所示:

[zy@fedora gdb_test]$ objcopy --add-gnu-debuglink=liba.so.debug liba.so
[zy@fedora gdb_test]$ objcopy --add-gnu-debuglink=test.debug test
[zy@fedora gdb_test]$ gdb ./test
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main () at main.c:7
7        int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        int main_b = 5;
(gdb) n
9        int main_sum = 0;
(gdb) n
11        main_a = main_a * main_a;
(gdb) c
Continuing.
main_sum = 90.

Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11        a = a * a;
(gdb) n
12        b = b * 2;
(gdb) 

为什么一定要使用–add-gnu-debuglink选项后才能看到调试信息呢?因为分离出单独的debuginfo文件后,文件名称是可以随意起的,gdb在调试程序的时候,怎么知道去哪个文件中找调试信息呢?这就需要在原来的程序中添加一些线索才行,这个线索就是调试文件的名称。其实现原理是在程序中有一个专门的段,叫做.gnu_debuglink段,用以记录debuginfo文件相关的信息,主要是文件名称和该文件的CRC校验码。如下所示:

[zy@fedora gdb_test]$ readelf -S liba.so|grep debug
  [26] .gnu_debuglink    PROGBITS         0000000000000000  00003160
[zy@fedora gdb_test]$ objdump -s -j .gnu_debuglink liba.so

liba.so:     文件格式 elf64-x86-64

Contents of section .gnu_debuglink:
 0000 6c696261 2e736f2e 64656275 67000000  liba.so.debug...
 0010 a1091f2f                             .../     
[zy@fedora gdb_test]$ readelf -S test|grep debug
  [28] .gnu_debuglink    PROGBITS         0000000000000000  00004768
[zy@fedora gdb_test]$ objdump -s -j .gnu_debuglink test

test:     文件格式 elf64-x86-64

Contents of section .gnu_debuglink:
 0000 74657374 2e646562 75670000 f2fa32cc  test.debug....2.

上面的objcopy --add-gnu-debuglink=liba.so.debug liba.so命令就是将指定的调试文件的名称(liba.so.debug)及其CRC校验值写入到liba.so文件中。但并未告诉gdb去哪个目录下找这个文件,gdb会去哪些路径下找这个文件呢?在网上找到如下一段话:

So, for example, suppose you ask GDB to debug /usr/bin/ls, which has a debug link that specifies the file ls.debug, and a build ID whose value in hex is abcdef1234. If the list of the global debug directories includes /usr/lib/debug, then GDB will look for the following debug information files, in the indicated order:

- /usr/lib/debug/.build-id/ab/cdef1234.debug
- /usr/bin/ls.debug
- /usr/bin/.debug/ls.debug
- /usr/lib/debug/usr/bin/ls.debug.
Global debugging info directories default to what is set by GDB configure option --with-separate-debug-dir and augmented by the colon-separated list of directories provided via GDB configure option --additional-debug-dirs. During GDB run you can also set the global debugging info directories, and view the list GDB is currently using.

set debug-file-directory directories
Set the directories which GDB searches for separate debugging information files to directory. Multiple path components can be set concatenating them by a path separator.

show debug-file-directory
Show the directories GDB searches for separate debugging information files.

这段话以ls命令做了一个举例。第一种build-id的方式不在本文阐述范围之内,是另一种寻找debuginfo文件的方法,本文不再描述。对于剩余的三个目录,有两个比较好理解,就是ls命令所在的目录、ls所在目录下的.debug目录去寻找对应的文件。第三个比较难理解,第三个是到/usr/lib/debug/目录下,然后根据ls目录对应的完整路径,到这个目录下去寻找。就是用/usr/lib/debug目录加上ls文件的实际路径进行拼接后的目录去寻找。比如一个可执行文件所在的目录是/home/test/example,那么gdb就回去/usr/lib/debug/home/test/下去寻找example.debug文件。再比如一个so库所在的目录是/home/libs/libxxx.so,那么gdb会去/usr/lib/debug/home/libs/目录下去寻找libxxx.so.debug文件。其它的也类似。就是将/usr/lib/debug目录当作根目录,然后在这个根目录下,去可执行文件所在的目录下去寻找对应的调试文件。因为ls命令在/usr/bin目录下,所以gdb才会去/usr/lib/debug/usr/bin目录下去寻找ls.debug文件。先做一个简单的实验,如下所示:

[zy@fedora debuginfo]$ ls
fff  test  test.debug
[zy@fedora debuginfo]$ gdb ./test
GNU gdb (Fedora Linux) 13.2-10.fc39
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
[zy@fedora debuginfo]$ ls -a
.  ..  .debug  fff  test
[zy@fedora debuginfo]$ ls .debug/
test.debug
[zy@fedora debuginfo]$ gdb ./test 
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/.debug/test.debug...
[zy@fedora debuginfo]$ pwd
/home/zy/debuginfo
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/test.debug 
/usr/lib/debug/home/zy/debuginfo/test.debug
[zy@fedora debuginfo]$ gdb ./test 
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /usr/lib/debug//home/zy/debuginfo/test.debug...

通过上面的例子中的Reading sysbols from …可以看到,能在对应的目录下读取到调试符号的信息。

对于依赖的so库,也是一样的道理,也会按照以上的路径去寻找so库所在的符号文件。如下所示:

[zy@fedora debuginfo]$ tree .
.
├── liba.so.debug
├── libs
│   ├── liba.so
│   └── liba.so.debug
├── test
└── test.debug


[zy@fedora debuginfo]$ gdb ./test
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 1, main () at main.c:7
7        int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        int main_b = 5;
(gdb) n
9        int main_sum = 0;
(gdb) c
Continuing.
main_sum = 90.

Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) 

gdb也可以支持不从默认的/usr/lib/debug目录中寻找,可以单独指定存放debuginfo文件的根目录。通过在gdb中的show debug-file-directory命令查看当前搜索的根目录。通过set debug-file-directory命令可以修改搜索的根目录。也支持设置多个根目录,如果有多个根目录,每个目录之间用路径分隔符“:“分开。如set debug-file-directory /home/test,将根目录设置为/home/test。set debug-file-directory /home/test:/root/test,将根目录设置为/home/test和/root/test。如下所示:

[zy@fedora debuginfo]$ gdb ./test 
GNU gdb (Fedora Linux) 13.2-10.fc39
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) show debug-file-directory 
The directory where separate debug symbols are searched for is "/usr/lib/debug".
(gdb) set debug-file-directory /usr/lib/debug/:/home/zy/debuginfo/debug_infos/
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11        a = a * a;
(gdb) n
12        b = b * 2;

如上所示,liba_func可以正常断住,可以看到调试信息。但test文件中的main函数,依然无法读取到调试信息,断点也并没有停住。这是什么原因呢?原来gdb程序在使用的时候,会主动从那几个位置读取被gdb的程序的调试信息,如果能读取到,就可以看到,如果读取不到,就认为读取失败。而执行set-debug-file-directory命令后,并不会再次去读取已经认为失败的可执行文件的调试信息,所以test的调试信息依然读取不到。而在执行set-debug-file-directory命令后才去加载的可执行文件,就能正确读取到调试信息。此种情况下,需要手动使用addd-symbol-file读取那些读取不到的可执行文件的调试信息。如下所示:

[zy@fedora debuginfo]$ gdb ./test 
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) set debug-file-directory /home/zy/debuginfo/debug_infos/
(gdb) add-symbol-file /home/zy/debuginfo/debug_infos/test.debug 
add symbol table from file "/home/zy/debuginfo/debug_infos/test.debug"
(y or n) y
Reading symbols from /home/zy/debuginfo/debug_infos/test.debug...
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) b main
Breakpoint 2 at 0x40113e: file main.c, line 7.
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 2, main () at main.c:7
7        int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        int main_b = 5;
(gdb) n
9        int main_sum = 0;
(gdb) c
Continuing.
main_sum = 90.

Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) 

GDB读取动态库的debuginfo文件时需要注意的地方

如前所述,如果将debuginfo文件直接放到/usr/lib/debug目录(或者通过set-debug-file-directory命令设置的根目录)下,也是可以的,但文件目录的组织结构必须和运行时的目录组织结构一样,这个地方有一个需要注意的地方,特别是对于so文件,在debug-root-directory目录下寻找对应的debuginfo文件时,和文件实际所在的位置不一定完全一致,而要看连接器能够找到的路径,即ldd所显示的路径。gdb是会到debug-root-directory/$LDD显示的目录下去寻找对应的debuginfo文件。而ldd显示出来的路径,可能是一个绝对路径,也可能是一个相对路径,者取决于你的操作系统中所设置的一些so查找路径,比如LD_LIBRARY_PATH设置为一个相对路径还是一个绝对路径,对于gdb来说,去debug-root-directory目录下寻找的路径也不一样。gdb会拿ldd显示出来的路径直接和debug-root-directory拼接一下。比如我当前的LD_LIBRARY_PATH设置为.fff,那么gdb就会去/usr/lib/debug/./fff目录下去读取liba.so所对应的debuginfo文件,因为ldd现实出来的liba.so的路径是./fff/liba.so。如下所示:

[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH 
./fff
[zy@fedora debuginfo]$ tree
.
├── fff
│   └── liba.so
└── test

2 directories, 2 files
[zy@fedora debuginfo]$ ldd test 
    linux-vdso.so.1 (0x00007ffda31b6000)
    liba.so => ./fff/liba.so (0x00007f0c0a237000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f0c0a03e000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f0c0a23e000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/fff/liba.so.debug 
/usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test 
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11        a = a * a;
(gdb) n
12        b = b * 2;
(gdb) 

如果我将LD_LIBRARY_PATH设置为该so所在的实际绝对路径/home/zy/debuginfo/fff/,那么gdb就会去/usr/lib/debug/home/zy/debuginfo/fff目录下去读取liba.so所对应的debuginfo文件,因为ldd显示出来的liba.so的路径是/home/zy/debuginfo/fff/liba.so。如下所示,debuginfo文件在/usr/lib/debug/fff/liba.so.debug时,无法读取到调试信息:

[zy@fedora debuginfo]$ export LD_LIBRARY_PATH=/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH 
/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ tree
.
├── fff
│   └── liba.so
└── test

2 directories, 2 files
[zy@fedora debuginfo]$ ldd test 
    linux-vdso.so.1 (0x00007ffc263bf000)
    liba.so => /home/zy/debuginfo/fff/liba.so (0x00007f98d6ead000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f98d6cb4000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f98d6eb4000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/fff/liba.so.debug 
/usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test 
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
Missing separate debuginfo for /home/zy/debuginfo/fff/liba.so.
The debuginfo package for this file is probably broken.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 1, 0x00007ffff7fbd10d in liba_func () from /home/zy/debuginfo/fff/liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function

而将调试文件移动到/usr/lib/debug/home/zy/debuginfo/fff/目录后,即可读取到正确的调试信息,如下所示:

[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH 
/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ ldd test 
    linux-vdso.so.1 (0x00007fff84fdf000)
    liba.so => /home/zy/debuginfo/fff/liba.so (0x00007f4e94e1d000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f4e94c24000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f4e94e24000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/fff/
liba.so.debug
[zy@fedora debuginfo]$ gdb ./test 
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);
(gdb) 

如果没有使用环境变量,而是程序中设置了rpath目录去读取动态库呢?其实是和环境变量时一样,都是根据ldd显示的路径去确定的。将liba.so.debug文件放到/usr/lib/debug/fff/目录下,通过设置rpath为$ORIGIN/fff,发现找不到调试信息,如下所示:

[zy@fedora debuginfo]$ patchelf --print-rpath test 
$ORIGIN/fff
[zy@fedora debuginfo]$ ldd test 
    linux-vdso.so.1 (0x00007ffef6ffc000)
    liba.so => /home/zy/debuginfo/./fff/liba.so (0x00007f1ea3ac9000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f1ea38d0000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f1ea3ad0000)
[zy@fedora debuginfo]$ ls -l /usr/lib/debug/fff/liba.so.debug 
-rwxr-xr-x. 1 root root 5544 11月21日 22:19 /usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test 
This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
Missing separate debuginfo for /home/zy/debuginfo/fff/liba.so.
The debuginfo package for this file is probably broken.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 1, 0x00007ffff7fbd10d in liba_func () from /home/zy/debuginfo/fff/liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function

因为ldd显示的liba.so的目录是/home/zy/debuginfo/./fff/liba.so,gdb在和debug-file-directory目录拼接后的路径是/usr/lib/debug/home/zy/debuginfo/./fff/,所以会去/usr/lib/debug/home/zy/debuginfo/./fff目录下寻找对应的debuginfo文件,如下所示:

zy@fedora debuginfo]$ patchelf --print-rpath test 
$ORIGIN/fff
[zy@fedora debuginfo]$ ldd test 
    linux-vdso.so.1 (0x00007fff495d5000)
    liba.so => /home/zy/debuginfo/./fff/liba.so (0x00007fbf93e95000)
    libc.so.6 => /lib64/libc.so.6 (0x00007fbf93c9c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fbf93e9c000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/fff/
liba.so.debug
[zy@fedora debuginfo]$ gdb ./test 
Reading symbols from ./test...

This GDB supports auto-downloading debuginfo from the following URLs:
  <https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.

Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6        int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8        sum = a + b;
(gdb) n
9        printf("%d + %d = %d.\n", a, b, sum);

可以看到能正确的找到调试信息了。

综上总结,gdb寻找单独的debug文件的路径是:

  1. 当前文件所在的目录。

  2. 当前文件所在目录下的.debug目录。

  3. debug-file-directory设置的目录下,根据文件所在的路径去寻找。如果是动态库文件,则会使用ldd所显示的目录。可以直接使用debug-file-directory目录+ldd显示目录进行拼接即可。

参考文档

https://stackoverflow.com/questions/36070648/search-symbols-again-after-setting-debug-file-directory

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Separate-Debug-Files.html

深入理解debuginfo(转载) - 简书

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值