Skip to content

GDB attach 时加载带调试信息的共享库符号

问题

进程运行时链接的是 stripped 的共享库(无调试信息),attach 后无法查看库函数的源码和断点信息。需要让 GDB 使用另一份带调试信息的 so 来加载符号。

以 libzmq 为例:进程加载的是 /lib/aarch64-linux-gnu/libzmq.so.5(无调试信息),而 /home/data/libzmq/ 下有编译时保留调试信息的版本。

关键认知

GDB attach 不能改变进程的库加载路径。 进程启动时 execve 和动态链接器已完成路径解析,/proc/<pid>/maps 中的映射不可更改。GDB 能做的只是替换符号文件的查找来源

方案对比

方案 原理 优缺点
set solib-search-path 按文件名在指定目录搜索符号文件 仅在原始路径文件不存在时回退搜索,本地文件存在时无效
set sysroot 路径前缀重定向 需要构造完整目录结构,影响所有库的查找
add-symbol-file 手动指定符号文件和加载地址 最直接,不影响其他库,推荐
临时替换原文件 替换系统目录下的 so 暴力有效,但影响全局,需恢复

推荐方案:add-symbol-file

步骤

  1. 查看目标库的加载地址
gdb
(gdb) info sharedlibrary libzmq
From                To                  Syms Read  Shared Object Library
0x0000ffffa18124e0  0x0000ffffa186f344  Yes (*)    /lib/aarch64-linux-gnu/libzmq.so.5

起始地址 0x0000ffffa18124e0 即 .text 段地址。

  1. 加载带调试信息的符号文件
gdb
(gdb) add-symbol-file /home/data/libzmq/libzmq.so.5 0x0000ffffa18124e0
add symbol table from file "/home/data/libzmq/libzmq.so.5" at
    .text_addr = 0xffffa18124e0
(y or n) y
Reading symbols from /home/data/libzmq/libzmq.so.5...
  1. 验证符号加载成功
gdb
(gdb) info functions zmq_send
All functions matching regular expression "zmq_send":

File /home/user/workspace/libzmq/src/zmq.cpp:
356:    int zmq_send(void*, void const*, size_t, int);
383:    int zmq_send_const(void*, void const*, size_t, int);
415:    int zmq_sendiov(void*, iovec*, size_t, int);
351:    int zmq_sendmsg(void*, zmq_msg_t*, int);

能列出源码位置和函数签名,说明符号生效。

  1. 正常调试
gdb
(gdb) b zmq_send
(gdb) b zmq.cpp:356
(gdb) list zmq_send

注意事项

  • info sharedlibrary 中该库仍显示 Yes (*),这是正常的——它反映的是进程原始加载的 so 的状态,不影响 add-symbol-file 附加的符号
  • 如果 info functions 找不到函数,说明指定的 so 本身也是 stripped 的,需要确认文件是否包含调试信息:
bash
readelf -S /home/data/libzmq/libzmq.so.5 | grep debug

solib-search-path 为什么无效

set solib-search-path 的工作机制:

  1. GDB 取进程已加载库的文件名(如 libzmq.so.5
  2. 先尝试原始绝对路径 /lib/aarch64-linux-gnu/libzmq.so.5
  3. 只有原始路径文件不存在时,才回退到 solib-search-path 指定的目录搜索

由于 /lib/aarch64-linux-gnu/libzmq.so.5 在本地存在,GDB 直接使用了它,solib-search-path 被跳过。

sysroot 方案(备选)

当需要批量替换多个库的符号时,构造 sysroot 目录结构更高效:

bash
# 构造与原始路径一致的目录结构
mkdir -p /home/data/sysroot/lib/aarch64-linux-gnu/
cp /home/data/libzmq/libzmq.so.5.2.2 /home/data/sysroot/lib/aarch64-linux-gnu/
cd /home/data/sysroot/lib/aarch64-linux-gnu/
ln -sf libzmq.so.5.2.2 libzmq.so.5
ln -sf libzmq.so.5 libzmq.so
gdb
(gdb) set sysroot /home/data/sysroot
(gdb) set solib-search-path /lib/aarch64-linux-gnu:/home/efort/lib

solib-search-path 作为补充,让 GDB 能找到 sysroot 中未覆盖的其他库。

精确获取 .text 段地址

info sharedlibrary 显示的起始地址通常就是 .text 段地址。如需精确确认:

bash
cat /proc/<pid>/maps | grep libzmq

输出中 r-xp(可读可执行)权限的行即为 .text 段:

ffffa18124e0-ffffa186f344 r-xp 00000000 ... /lib/aarch64-linux-gnu/libzmq.so.5

总结

  • attach 后无法改变进程的库加载路径,只能替换 GDB 的符号查找来源
  • add-symbol-file 是最干净的方案:一行命令,不影响其他库,不需要修改任何文件
  • info sharedlibrary(*) 标记不影响实际调试,以 info functions 和断点设置验证为准
  • solib-search-path 在原始路径文件存在时不会生效,这是最常见的踩坑点

基于 VitePress 构建