根本原因是dwarf调试信息中存储的是绝对路径,而本地不存在该路径;应使用set substitute-path进行路径映射,而非dir命令,且需在target remote后、list前执行。

GDB远程连接后为什么没有源码——根本原因不是网络或gdbserver没配好,而是源码路径在可执行文件的DWARF调试信息里存的是**绝对路径**,而远程目标机上根本不存在那个路径。GDB客户端(本地)不会自动去猜、也不会主动拉取源码,它只按调试信息里记录的路径去本地文件系统找。
为什么list报“没有那个文件或目录”
典型错误现象:(gdb) list 或 (gdb) list main 返回类似 main.c: 没有那个文件或目录,但你确认本地有这份源码,且 file 已正确加载了远程编译的可执行文件。
- 可执行文件里保存的不是源码内容,而是编译时的完整绝对路径(比如
/home/build/project/src/main.c) - 远程调试时,gdbserver 只负责转发运行状态和内存数据,不传源码;gdb 客户端默认只查自己机器上的那个绝对路径
- 如果本地开发机上源码放在
~/src/project/main.c,而调试信息里记的是/home/build/...,GDB 就会直接失败,连提示都懒得换行
用set substitute-path映射路径差异
这是最干净、最推荐的解法,尤其适合 CI 编译机 + 本地开发机的场景。它告诉 GDB:“调试信息里写的路径前缀 A,实际对应我本地的路径 B”。
- 语法是
set substitute-path <from-path><to-path></to-path></from-path>,必须在target remote之后、list之前执行 - 例如远程编译路径是
/opt/build/app/,你本地放到了~/workspace/app/,就输:set substitute-path /opt/build/app/ ~/workspace/app/ - 支持多次设置,应对多个源码根目录;用
show substitute-paths查看当前生效的映射 - 注意末尾斜杠:路径必须严格匹配前缀,
/opt/build/app和/opt/build/app/是不同的字符串
别依赖dir命令硬加搜索路径
dir 看起来简单,但它只对“文件名相同但路径完全丢失”的情况有效(比如调试信息里只存了 main.c,没存路径),而绝大多数现代编译器(GCC/Clang)默认写入的是绝对路径,dir 对这种情形完全无效。
-
dir ~/src不会让 GDB 去~/src/main.c找 —— 它只尝试把main.c当作相对路径拼到~/src/后面,但调试信息里存的是/home/build/src/main.c,根本不会触发匹配 -
set directories是dir的等价命令,行为一致,别被名字误导 - 如果你真想靠
dir解决问题,得先用readelf -w ./a.out | grep DW_AT_comp_dir确认调试信息里存的确实是相对路径(极少见)
远程调试时最容易忽略的细节
路径映射只是第一步。真正卡住人的往往是后续环节没对齐:
- 确保
file加载的是和远程gdbserver正在跑的**同一个可执行文件**(校验md5sum或sha256sum,别只看文件名) - 如果用了交叉编译,本地
gdb必须是对应架构的版本(如aarch64-linux-gnu-gdb),否则即使路径对了,符号解析也会失败 -
set substitute-path不会递归重写嵌套路径(比如头文件里的#include "../inc/log.h"),如果头文件也找不到,得单独再加一条映射











