ubuntu18.04 环境下编译支持 debuginfod 的 gdb

介绍

Ubuntu 22.10 版本才默认安装 debuginfod,对于之前的发行版都需要手动配置。gdb 从 10.1 版本才开始支持 debuginfod,而 Ubuntu 旧的发行版里 gdb 都低于 10.1 版本。另外,debuginfod 被包含在 elfutils 里面,且从 elfutils-0.178 版本后才支持,而 Ubuntu 旧的发行版里 elfutils 版本都低于 0.178,因此需要先源码编译安装高版本 elfutils,再编译安装高版本 gdb,最终才能实现在 Ubuntu 旧的发行版中使用支持 debuginfod 的 gdb 来调试程序。

注意:gdb-10.1 及以后版本要求 libdebuginfod >= 0.179,所以 elfutils 最低版本为 0.179。

debuginfod_demo

编译安装 elfutils-0.179

  1. 下载 elfutils-0.179:
1
wget https://sourceware.org/elfutils/ftp/0.179/elfutils-0.179.tar.bz2
  1. 解压后, 通过./configure && make && make install 编译安装,这里我手动指定了安装路径:
1
./configure --prefix=/home/geduer/elfutils-0.179
  1. 在 configure 时会遇到下面报错:
1
2
3
4
5
checking for libmicrohttpd... no
checking for libcurl... no
checking for sqlite3... no
checking for libarchive... no
configure: error: C++ compiler or dependencies not found, use --disable-debuginfod to disable.

原因是系统下缺少 libmicrohttpd、libcurl 、sqlite3、libarchive 这四个依赖库,通过 apt 来安装这些依赖库,还需要安装对应的 dev 开发包,所以选择直接安装 dev 包,默认会把依赖库也安装。

1
2
3
4
sudo apt-get install libcurl4-openssl-dev
sudo apt-get install libsqlite3-dev
sudo apt-get install libarchive-dev
sudo apt-get install libmicrohttpd-dev
  1. 安装好依赖库后再 configure 就不报错了,最后会显示 elfutils 配置清单,可以看到此时已支持 Debuginfod 特性:
1
2
3
4
5
6
        elfutils: 0.179 (eu_version: 179)
......
OTHER FEATURES
......
Debuginfod client/server support : yes
......
  1. 最后把自定义的安装路径添加到系统环境变量里,这样启动 gdb 时才能识别到 debuginfod 库。
1
2
export PATH=/home/geduer/elfutils-0.179/bin:$PATH
export LD_LIBRARY_PATH=/home/geduer/elfutils-0.179/lib:$LD_LIBRARY_PATH

编译安装 gdb-13.1

  1. 下载源码
1
wget https://ftp.gnu.org/gnu/gdb/gdb-13.1.tar.gz
  1. 编译安装
1
2
./configure --prefix=/home/geduer/gdb/install --enable-tui --with-debuginfod
make && make install

虽然已经安装了 debuginfod,但是 gdb 编译还是同样报错:

1
2
3
4
5
checking for aarch64-unknown-linux-gnu-pkg-config... /usr/bin/aarch64-unknown-linux-gnu-pkg-config
checking pkg-config is at least version 0.9.0... yes
checking whether to use debuginfod... yes
checking for libdebuginfod >= 0.179... no
configure: error: "--with-debuginfod was given, but libdebuginfod is missing or unusable."

排查发现,gdb 编译时识别到 --with-debuginfod 选项后,会通过 pkg-config 工具来查找 debuginfod 依赖库。pkg-config 是通过查找后缀名为 pc 的配置文件判断依赖库是否存在,结果没找到 libdebuginfod.pc 文件,就认为缺少 debuginfod 库。可以从 gdb/configure 里截取相关命令来手动检测:

1
2
3
4
5
$ /usr/bin/aarch64-unknown-linux-gnu-pkg-config --exists --print-errors "libdebuginfod >= 0.179"
Package libdebuginfod was not found in the pkg-config search path.
Perhaps you should add the directory containing `libdebuginfod.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libdebuginfod' found

然后到 debuginfod 安装目录查找一下,发现配置文件路径为:lib/pkgconfig/libdebuginfod.pc。最后需要把包含的 libdebuginfod.pc 路径导入到环境变量 PKG_CONFIG_LIBDIR

1
export PKG_CONFIG_LIBDIR=/home/geduer/elfutils-0.179/lib/pkgconfig

注意:如果把 libdebuginfod.pc 路径导入到环境变量 PKG_CONFIG_PATH,结果还是会识别不到。查 pkg-config 手册发现 PKG_CONFIG_LIBDIR 比 PKG_CONFIG_PATH 优先级更高,PKG_CONFIG_PATH 只是附加一个搜索路径,PKG_CONFIG_LIBDIR 则是覆盖默认的搜索路径。如果嫌配置麻烦,可以在安装 elfutils 时,选择默认安装路径,即 /usr/local 目录,pkg-config 默认会搜索 /usr/local 目录。

可以通过下面命令测试下是否能成功搜索到 pc 配置文件(echo $? 结果为 0 表示执行结果正确,为 1 表示错误):

1
2
3
pkg-config --exists libdebuginfod
echo $?
0

debuginfod 的使用方法

配置 debuginfod 服务器地址

首先配置 debuginfod 服务器地址 https://debuginfod.ubuntu.com ,将其添加到 ~/.bashrc 里:

1
export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com"

当使用 gdb 调试程序时,会通过 http 远程访问 debuginfod 服务器,根据程序唯一的 build-id 下载对应的调试符号和源文件(前提时服务器里有对应的符号文件和源码包)到本地 ~/.cache/debuginfod_client 目录。

gdb 中 debuginfod 命令

启动 gdb-13.1,通过命令 show debuginfod 查看 debuginfod 配置,主要包括三个配置:

  • 每次启动 gdb 会主动询问是否开启 debuginfod 功能,可以把命令 set debuginfod enabled on 添加到 ~/.gdbinit 里永久开启;
  • 设置 debuginfod 服务器地址,不同发行版厂商会维护自己的 debuginfod 服务器。
  • 打开 debuginfod 运行时的详细输出信息,可以通过命令 set debuginfod verbose 0 关闭符号下载过程信息打印。
1
2
3
4
5
(gdb) show debuginfod 
debuginfod enabled: Debuginfod functionality is currently set to "ask".
debuginfod urls: Debuginfod URLs are currently set to:
https://debuginfod.ubuntu.com
debuginfod verbose: Debuginfod verbose output is set to 1.

gdb 里和 debuginfod 相关的命令如下,主要用来设置和显示上述的三个配置。

1
2
3
4
5
6
7
8
9
10
(gdb) apropos debuginfod
set debuginfod -- Set debuginfod options.
set debuginfod enabled -- Set whether to use debuginfod.
set debuginfod urls -- Set the list of debuginfod server URLs.
set debuginfod verbose -- Set verbosity of debuginfod output.
show debuginfod -- Show debuginfod options.
show debuginfod enabled -- Show whether to use debuginfod.
show debuginfod urls -- Show the list of debuginfod server URLs.
show debuginfod verbose -- Show debuginfod debugging.
(gdb)

使用演示

  1. 通过 gdb 调试 git,启动后输入 y 启用 debuginfod 后便自动下载调试符号:
1
2
3
4
5
6
7
8
9
geduer@gdk8:~/gdb/install/bin$ ./gdb /usr/bin/git -q
Reading symbols from /usr/bin/git...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.ubuntu.com>
Enable debuginfod for this session? (y or [n]) y
Debuginfod has been enabled.
To make this setting permanent, add 'set debuginfod enabled on' to .gdbinit.
Downloading separate debug info for /usr/bin/git
[#################################### ] 32% (4.19 M)
  1. 下载完后会自动读取符号文件,设置 main 断点后提示源码下载失败,这因为 debuginfod 是新特性,所以 debuginfod 服务器里对旧版 Ubuntu 的调试符号和源码支持不够完善,这里需要我们自己手动下载源码,并添加到 gdb 源码搜索路径里:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   Reading symbols from /home/geduer/.cache/debuginfod_client/f1b3574e4c6c4600819de4fe860b1912235a8be3/debuginfo...                     
(gdb) b main
Download failed: Invalid argument. Continuing without source file ./common-main.c.
Breakpoint 1 at 0x17108: file common-main.c, line 27.
(gdb) !sudo apt source git
Reading package lists... Done
NOTICE: 'git' packaging is maintained in the 'Git' version control system at:
https://repo.or.cz/r/git/debian.git/
Please use:
git clone https://repo.or.cz/r/git/debian.git/
to retrieve the latest (possibly unreleased) updates to the package.
Skipping already downloaded file 'git_2.17.1-1ubuntu0.18.dsc'
Skipping already downloaded file 'git_2.17.1.orig.tar.xz'
Skipping already downloaded file 'git_2.17.1-1ubuntu0.18.debian.tar.xz'
Need to get 0 B of source archives.
Skipping unpack of already unpacked source in git-2.17.1
(gdb) directory git-2.17.1/
Source directories searched: /home/geduer/gdb/install/bin/git-2.17.1:$cdir:$cwd
  1. 开始调试运行程序,debuginfod 会自动判断下载的其他依赖库的调试符号:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(gdb) run
Starting program: /usr/bin/git
Downloading separate debug info for /lib/ld-linux-aarch64.so.1
Downloading separate debug info for system-supplied DSO at 0x7ff7ffc000
Downloading separate debug info for /lib/aarch64-linux-gnu/libpcre.so.3 Downloading separate debug info for /lib/aarch64-linux-gnu/libz.so.1 Downloading separate debug info for /lib/aarch64-linux-gnu/libpthread.so.0 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/aarch64-linux-gnu/libthread_db.so.1".
Downloading separate debug info for /lib/aarch64-linux-gnu/libc.so.6

Breakpoint 1, main (argc=1, argv=0x7ffffff3d8) at common-main.c:27
warning: Source file is more recent than executable.
27 {
(gdb) l
22 sigprocmask(SIG_UNBLOCK, &unblock, NULL);
23 signal(SIGPIPE, SIG_DFL);
24 }
25
26 int main(int argc, const char **argv)
27 {
28 /*
29 * Always open file descriptors 0/1/2 to avoid clobbering files
30 * in die(). It also avoids messing up when the pipes are dup'ed
31 * onto stdin/stdout/stderr in the child processes we spawn.
  1. 还可以根据源文件和行号设置断点,注意自己下载的源码和系统下的二进制可能不是完全匹配,基于符号调试时可能源码行号对应信息有偏移误差,自行诊断:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(gdb) b git.c:666
Breakpoint 2 at 0x555556d49c: file git.c, line 666.
(gdb) c
Continuing.

Breakpoint 2, cmd_main (argc=<optimized out>, argc@entry=1, argv=<optimized out>, argv@entry=0x7ffffff3d8) at git.c:666
warning: Source file is more recent than executable.
666 commit_pager_choice();
(gdb) n
667 printf("usage: %s\n\n", git_usage_string);
(gdb) p git_usage_string
$2 = 0x55556f8870 <git_usage_string> "git [--version] [--help] [-C <path>] [-c <name>=<value>]\n", ' ' <repeats 11 times>, "[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n", ' ' <repeats 11 times>, "[-p | --paginate | --no-pager] [--no-replace-objects] [--"...
(gdb) n
usage: git [--version] [--help] [-C <path>] [-c <name>=<value>]
[--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
[-p | --paginate | --no-pager] [--no-replace-objects] [--bare]
[--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
<command> [<args>]

668 list_common_cmds_help();
(gdb)

Ubuntu 22.xx 安装方法

Ubuntu22.xx 版本系统仓库里有 debuginfod,可以直接 apt 安装:

1
sudo apt install debuginfod

然后在 ~/.bashrc 里配置服务器地址即可:

1
export DEBUGINFOD_URLS="https://debuginfod.ubuntu.com"

Ubuntu 23.xx 最新版本自带的 gdb 版本为 13.1,默认支持 debuginfod,可以无需任何操作即可使用。