🐂这里是「你牛克斯」: 专注于分享Linux生鲜,记得关注我
一、最基础的概念(看懂分工)
GDB Server(远程调试服务)的调试机制核心在于“控制与通信的分离”。
它采用典型的 C/S(客户端/服务器)架构:在宿主机(Host)上运行功能完整的 GDB 作为客户端(负责提供用户界面、解析符号表等复杂工作),而在目标机(Target)上运行轻量级的 GDB Server(负责直接控制目标进程并读写内存)。两者之间通过 RSP(Remote Serial Protocol,远程串行协议) 协议进行网络或串口通信。
在这个最简单的模型里,你只需要明白一件事:复杂的工作交给开发机(Host),脏活累活交给目标机(Target)。
Host 端: 你的电脑,上面运行着完整的 GDB 软件。因为你的电脑配置高、内存大,所以它负责加载代码的各种符号表(比如哪一行代码对应哪个内存地址)。
Target 端: 远端的服务器或者嵌入式板子,上面只运行一个非常小的 gdbserver 软件和你的目标程序。它不需要懂代码逻辑,只听从 Host 的指挥。

二、引入协议(看懂怎么交流)
Host 和Guest之间的沟通语言——RSP 协议(Remote Serial Protocol)。
你在电脑上输入命令(比如单步执行 step 或者看寄存器),GDB 会把这些人类语言翻译成类似 $vCont;s#0a 这种极简的加密文本,通过网线(TCP/IP)或者串口发给远端的 gdbserver。
gdbserver 收到后照办,并把结果同样用加密文本回传给你的电脑。
三、深入目标机内部(看懂内核如何参与)
把目光移到目标机(Target)的内部。gdbserver 是怎么控制另一个程序的?
这里引入了 Linux 内核的大杀器:ptrace 系统调用。
gdbserver 本身没有特权,它必须通过向系统内核申请 ptrace,让内核帮它把目标程序“接管”过来。一旦接管,目标程序的一举一动(比如遇到了断点、崩溃了)都会先被内核拦下来,报告给 gdbserver。

四、完整的闭环流
你在电脑上点“加断点” → GDB 发送网络包 → gdbserver 收到后通过 ptrace 修改目标程序的内存(埋入陷阱) → 目标程序运行到这里触发硬件异常 → 内核拦截并挂起程序 → 内核通知 gdbserver → gdbserver 通过网络包告诉你的电脑 → 你的电脑屏幕上显示“程序已暂停”。
五、实操部分
Host端 :基于X86的架构的Linux容器开发环境
开发板是全志的,全志官方推荐的开发环境是Ubuntu 14.04,我这边用的稍微有点高,但是目前看起来没问题。
user@67c101d9d32e:/home/work$ cat /etc/issueUbuntu 20.04.6 LTS \n \l
需要安装一些32位应用程序以及开发包(SDK里面带了一些ELF,居然是32位的
user@67c101d9d32e:/home/work$ file /home/work/out/host/bin/fsbuild/home/work/out/host/bin/fsbuild: ELF 32-bit LSB executable, Intel 80386, \version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, \for GNU/Linux 2.6.9, with debug_info, \not strippe
所以需要安装一些依赖
sudo dpkg --add-architecture i386sudo apt updatesudo apt install -y libc6:i386 libstdc++6:i386 zlib1g:i386sudo apt install -y libncurses5:i386 libgcc-s1:i386
由于主控芯片是玄铁的,TinaSDK里面的交叉编译器是来自玄铁的工具链

TinaSDK 里面有对应的编译入口,位于
user@67c101d9d32e:/home/work$ ls package/devel/gdb/Makefile patches
编译入口也支持GUI进行配置(make menuconfig)

source build/envsetup.shlunch d1-h_nezha_minmake menuconfiguser@67c101d9d32e:/home/work$ grep gdb .configCONFIG_PACKAGE_gdb is not set#确保这里打开了CONFIG_PACKAGE_gdbserver=yCONFIG_PACKAGE_python-gdbm is not setCONFIG_PACKAGE_python3-gdbm is not setCONFIG_PACKAGE_libgdbm is not set#单独编译这个应用程序,本质上是通过交叉编译器把gdb-server的源码编译到目标架构,可以是ARM/RISC-Vmake package/devel/gdb/install#也可以通过make 全部编译一遍make -j# 编译产物./out/d1-h-nezha_min/compile_dir/target/gdb-10.1/ipkg-sunxi/gdbserver/usr/bin/gdbserver
此外需要准备一个C/C++程序,用于远程调试,-g 参数表示保留调试符号,-O0表示不要把整型等小变量优化掉了-march 后面那一串是说我用到了一些向量化的函数库,c906是我的开发板的主核心芯片型号,这个经过玄铁定制的编译器可能做了一些硬件优化,-static表示静态编译(独立应用程序)。
riscv64-unknown-linux-gnu-gcc \-g -O0 -mcpu=c906fdv \-march=rv64imafdcv0p7_zfh_xtheadc \-mabi=lp64d -mtune=c906 -static hellomain.c -o build/test
Target端:基于RISC-V架构的单核开发板(全志D1-H 哪吒)

开发板需要配置好网络,我的Host端是通过局域网(Wifi或者网线)连接到了Target端。

中转端: Windows
这个环境是可选的,直接在Linux环境也能全部搞定(实际情况比较复杂,比如,可能有些开发板厂商的emmc/nand刷机程序仅支持Windows,所以Windows环境虽然烂,但是办公和工作都需要)。
我的情况是:通过 adb 协议(可能需要安装硬件厂家的USB驱动才行)把编译好的二进制拷贝到开发板,如果开发板不支持这个协议,也可以通过U盘拷贝、ftp协议传输,甚至scp,但是这些服务可能需要单独在开发板的编译环境开启才行。
需要注意的是,各种ELF文件在windows 躺一遍之后,权限莫名其妙的消失了,所以需要在开发板追加需要的权限
adb push gdbserver /usr/bin/adb shellchmod +x /usr/bin/gdbserver

ssh也好,adb也罢,容易掉线,保险一点,还是把串口接上,可以通过Putty或者 MobaXterm软件和开发板交互,当然,又涉及到了驱动(需要安装串口线里面那颗控制芯片的厂家的驱动)。


六、八哥/BUG
不知道是什么原因,我这里的gdb 里面无法打印某些变量的值,AI告诉我说gdb 10.1版本太老了,不识别RISC-V架构的一些寄存器,试了一下确实找不到v0~v31这些向量寄存器,但是整型变量也无法打印,感觉难以理解。
riscv64-unknown-linux-gnu-gdb ./build/testb maintarget remote 192.168.0.100:2234cdemo_rvv_vxrm () at hellomain.c:218218 int16_t src[4] = {103,107,111,115};(gdb) n220 size_t vl = 4;(gdb) p src$4 = <unavailable>(gdb) p $v0$5 = void
参考链接
https://aosabook.org/en/v2/gdb.html
如果觉得今天这篇“夸自己聪明”的摸鱼干货对你有启发,别忘了顺手点个【赞】和【在看】,或者赶紧【收藏】起来,下次搬砖时直接复制粘贴!