gdbserver远程调试中动态库断点失效需手动加载符号:先运行至dlopen后,用info sharedlibrary确认库已加载(显示Yes)并获取基址,再add-symbol-file指定路径和地址加载符号;solib-search-path仅对后续库生效,不可用于已加载库。

gdbserver 启动时动态库还没加载,断点会失效
远程调试中,gdbserver 启动时只加载主程序,而 dlopen 动态加载的库(如 libplugin.so)在运行时才映射进内存。此时如果提前用 b libplugin.so:my_func 设置断点,GDB 会报 Function not defined 或静默忽略——因为库文件尚未被 info sharedlibrary 列出。
关键在于:GDB 不会自动监听后续 dlopen 行为,必须等库加载完成后,再手动加载符号。
- 先让程序运行到
dlopen调用之后(比如在调用后加个usleep(100000)方便停住) - 在 GDB 中执行
info sharedlibrary,确认目标库已出现在列表里(状态为Yes) - 若仍显示
No,说明符号路径不对,需补set solib-search-path指向含调试信息的.so文件所在目录 - 然后用
add-symbol-file手动加载符号:add-symbol-file /path/to/libplugin.so 0x7f8a200000(地址从info sharedlibrary输出中获取)
如何获取动态库的加载基址
info sharedlibrary 是唯一可靠方式。它输出每条库记录包含三列:是否已加载(Yes/No)、加载起始地址、库路径。例如:
From To Syms Read Shared Object Library 0x7f8a200000 0x7f8a201240 Yes /tmp/libplugin.so
其中 0x7f8a200000 就是该库的运行时基址,必须传给 add-symbol-file,否则函数地址计算错误,断点跳转失败。
- 不要依赖
readelf -l查看 ELF 的LOAD段——实际加载地址由内核 ASLR 决定,每次不同 - 不要用
nm -D查符号偏移后硬算——缺少重定位修正,容易错位 - 如果库被多次
dlopen(如插件热重载),每次加载基址都不同,需重新info sharedlibrary+add-symbol-file
调试符号缺失时的 fallback 方案
当目标设备上只有 stripped 的 .so(无调试信息),但你本地有带 -g 编译的同版本库,可强制用本地符号对齐远程地址:
add-symbol-file /host/path/libplugin.so 0x7f8a200000 \ -s .text 0x7f8a200000 \ -s .data 0x7f8a201000
这要求你知道各 section 在内存中的真实位置,可通过 cat /proc/<pid>/maps 在目标设备上查到(需在 dlopen 后立刻抓取)。
-
/proc/<pid>/maps输出中找对应库名行,第二列即为.text起始地址 - 若库未启用
-fPIC,add-symbol-file会报错,此时只能反汇编调试:disassemble my_func+info registers - Android 设备上注意 SELinux 可能禁止读取
/proc/<pid>/maps,需先adb shell setenforce 0
常见陷阱:solib-search-path 不生效
set solib-search-path 只影响后续自动加载的库,对已加载但无符号的库无效。典型错误是:
- 先运行
continue让程序走到dlopen后,再执行set solib-search-path—— 此时库已加载,GDB 不会重新解析符号 - 路径中混用软链接,而 GDB 解析的是链接目标路径,不是你写的路径字面量
- 交叉调试时路径指向了 x86_64 版本的库,但目标是 aarch64,
add-symbol-file会拒绝加载
验证是否生效:执行 info sharedlibrary 后,观察目标库右侧是否从 No 变成 Yes;若仍是 No,说明路径或架构不匹配,得换方案。


















