通过仓库源安装 glibc 符号来实现 gdb 调试

安装符号

从仓库源里,安装官方发布的符号包,可以先模糊搜索下,找下 “debug symbols for glibc……” 类似的关键字介绍,从而确定符号包的具体名字。比如 deb 包为 libc6-dbg,rpm 包为 glibc-debugutils,不同的操作系统发行版名字会有变动,比如有些包名为 libc6.1-dbgsym。

1
sudo apt install libc6-dbg

符号信息会安装在 /usr/lib/debug/.build-id 目录下,以 build-id.debug 来命名。build-id 是 gcc 编译二进制的时候计算的文件标识,类似文件哈希值。
可以通过 readelf 和 file 命令来查看:

1
2
lhx@ubuntu:~$ file a.out 
a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=f27b6b8365c516b14ed4bf830e6a80bf28756456, for GNU/Linux 3.2.0, with debug_info, not stripped
1
2
3
4
5
6
7
8
lhx@ubuntu:~$ readelf -n a.out 
……
Displaying notes found in: .note.gnu.build-id
所有者 Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: f27b6b8365c516b14ed4bf830e6a80bf28756456
……
lhx@ubuntu:~$

验证版本是否对应

对比一下 build-id,然后 find 查找 build-id.debug 文件时,注意去掉前 2 位,前 2 位为目录名称。

1
2
3
4
5
6
lhx@ubuntu:/usr/lib/debug$ file /lib/x86_64-linux-gnu/libc-2.31.so 
/lib/x86_64-linux-gnu/libc-2.31.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1878e6b475720c7c51969e69ab2d276fae6d1dee, for GNU/Linux 3.2.0, stripped
lhx@ubuntu:/usr/lib/debug$
lhx@ubuntu:/usr/lib/debug$ find -name 78e6b475720c7c51969e69ab2d276fae6d1dee.debug
./.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug
lhx@ubuntu:/usr/lib/debug$

拉取源码

前提是在 /etc/apt/sources.list 中已经配置过 dbkg-src,这样才能通过 apt 下载源码,源码会下载到当前目录并自动解压。

1
sudo apt source libc6-dev

开始 gdb 调试

前面准备工作做好后,下面开始 gdb 调试,默认情况下此时 gdb 没有找到 glibc 的符号信息 (No debugging symbols found in /lib/x86_64-linux-gnu/libc.so.6):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
lhx@ubuntu:~$ gdb a.out 
Reading symbols from a.out...
(gdb) set verbose on
(gdb) b 5
Reading in symbols for hello.c...
Breakpoint 1 at 0x1151: file hello.c, line 5.
(gdb) r
Starting program: /home/lhx/a.out
Using PIE (Position Independent Executable) displacement 0x555555554000 for "/home/lhx/a.out".
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
Reading symbols from system-supplied DSO at 0x7ffff7fcd000...
(No debugging symbols found in system-supplied DSO at 0x7ffff7fcd000)
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
(No debugging symbols found in /lib/x86_64-linux-gnu/libc.so.6)

Breakpoint 1, hello () at hello.c:5
5 printf("Hello World !\n");
(gdb)

查看下 gdb 符号搜索路径是否正确,不同的话,需要手动设置 debug-file-directory 后才能找到符号信息,可以看到已经成功找到了:Reading symbols from /usr/lib/debug/.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug…。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(gdb) show debug-file-directory 
The directory where separate debug symbols are searched for is "/usr/local/lib/debug".
(gdb) set debug-file-directory /usr/lib/debug/
(gdb) r
Starting program: /home/lhx/a.out
Using PIE (Position Independent Executable) displacement 0x555555554000 for "/home/lhx/a.out".
Reading symbols from /lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/45/87364908de169dec62ffa538170118c1c3a078.debug...
Reading symbols from system-supplied DSO at 0x7ffff7fcd000...
(No debugging symbols found in system-supplied DSO at 0x7ffff7fcd000)
Reading in symbols for rtld.c...
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug...

Breakpoint 1, hello () at hello.c:5
5 printf("Hello World !\n");

找到符号信息后,就可以在 printf 函数处单步调试进入,这时提示 ioputs.c 文件找不到,需要去 glibc 源码目录下 find 一下该文件,然后把文件目录通过 dir 命令添加进去。

到此就大功告成了,从此可以自由自在的调试 glibc 的源码啦!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(gdb) s
Reading in symbols for ioputs.c...
__GI__IO_puts (str=0x555555556004 "Hello World !") at ioputs.c:33
33 ioputs.c: 没有那个文件或目录.
(gdb) dir ~/test1/glibc-2.31/libio
Source directories searched: /home/lhx/test1/glibc-2.31/libio:$cdir:$cwd
(gdb) n
35 size_t len = strlen (str);
(gdb) l
30
31 int
32 _IO_puts (const char *str)
33 {
34 int result = EOF;
35 size_t len = strlen (str);
36 _IO_acquire_lock (stdout);
37
38 if ((_IO_vtable_offset (stdout) != 0
39 || _IO_fwide (stdout, -1) == -1)
(gdb) bt
#0 __GI__IO_puts (str=0x555555556004 "Hello World !") at ioputs.c:35
#1 0x000055555555515d in hello () at hello.c:5
Reading in symbols for ../sysdeps/x86/libc-start.c...
#2 0x0000555555555172 in main () at hello.c:10
(gdb)