断点提示“Function not defined”通常因动态库未加载,需先 run 再 info sharedlibrary 确认;可用 pending 断点提前设置;symbol-file 与 sharedlibrary 行为不同,须按场景选用;调试符号必须编译时加 -g,动态库必须 -fPIC。

断点提示“Function not defined”时别急着删重试
这通常不是你写错了函数名,而是动态库还没加载进内存——info sharedlibrary 显示 No shared libraries loaded at this time 就是铁证。GDB 启动后默认只加载主程序符号,.so 是运行时由动态链接器(如 ld-linux-x86-64.so.2)按需载入的,早于 main 执行也可能没到那步。
真正该做的只有两件事:
- 先
run让程序跑起来,等它自己把依赖的库加载完; - 再立刻执行
info sharedlibrary确认目标库是否显示Yes(符号已读); - 如果仍显示
No,说明符号表缺失,得手动补:symbol-file /path/to/libxxx.so或sharedlibrary libxxx.so(后者要求库名在搜索路径中)。
用 pending 断点省去反复 run + break
你完全可以在程序启动前就下断点,哪怕函数还不存在。GDB 会自动把它标记为 pending:
(gdb) b mylib_initFunction "mylib_init" not defined.Make breakpoint pending on future shared library load? (y or [n]) y
这个机制依赖 GDB 对 _dl_debug_state 的监控——每次动态库加载,GDB 都能捕获并检查 pending 列表。但注意:
- 仅对
ldd能列出的静态依赖有效;dlopen("xxx.so", RTLD_NOW)这种运行时打开的库,GDB 也能感知,前提是没跳过初始化节(比如__attribute__((constructor))函数); - 如果断点始终不触发,先
info breakpoints看状态是不是 still pending,再info sharedlibrary确认库是否真被加载了; - 别指望 pending 断点能跨 GDB 会话保留——退出再进就得重设。
symbol-file 和 sharedlibrary 别混用
这两个命令看着都像“加符号”,但行为完全不同:
-
sharedlibrary libxxx.so:只在当前已加载的库中匹配文件名,然后加载其符号(要求库已映射进地址空间,且路径在solib-search-path里); -
symbol-file /full/path/to/libxxx.so:强制把指定文件当符号表读进来,不管它是否已被加载、甚至是否存在对应内存段——适合调试符号分离场景(比如objcopy --only-keep-debug出来的.debug文件); - 误用
symbol-file加一个未加载的库,会导致 GDB 把断点插到错误地址(因为没基址偏移校准),后续run可能直接 crash 或断点失效。
稳妥做法是:先 set solib-search-path /your/lib/dir,再 sharedlibrary libxxx;若路径不标准,就 symbol-file + add-symbol-file(后者需手动传加载基址,从 info sharedlibrary 输出里抄)。
调试符号必须编译时带上 -g,-fPIC 不是可选项
没 -g,info functions 查不到函数,list 看不到源码,break 只能靠地址硬猜——这不是调试,是考古。
而漏掉 -fPIC 的后果更隐蔽:编译能过,ldd 能看到库,但 GDB 加载符号时可能报 Cannot access memory 或断点地址错乱。因为非 PIC 的代码没法在任意地址重定位,GDB 按照加载地址反推符号位置会失败。
验证方式很简单:
- 用
readelf -h libxxx.so | grep Type确认是DYN (Shared object file); - 用
file libxxx.so看是否含with debug_info; - 最直觉的是
gdb ./main后symbol-file ./libxxx.so不报错,且info functions myfunc能列出来。
动态库调试真正的复杂点不在命令怎么输,而在符号、加载时机、内存布局三者必须对齐——少一个,GDB 就只能给你看汇编和地址,而不是你想看的那行 C++ 代码。


















