GNU Debugger(GDB)使用教程
一、GDB介绍
1、什么是GDB
(1)GDB是GNU项目的开源、源代码级命令行调试器,可控制程序的启动、暂停和终止,并在程序暂停时查看内部变量、寄存器、调用栈等运行现场,供开发者定位代码问题。
(2)GDB支持绝大多数主流UNIX、Windows、macOS操作系统;嵌入式开发使用对应架构的交叉版GDB(如ARM Cortex-M系列arm-none-eabi-gdb)。
(3)GDB支持四类调试操作:
①加载/启动目标程序,配置运行参数、环境变量,控制程序执行流程。
②设置代码断点和观察点,满足特定触发条件时自动暂停程序。
③程序主动暂停、断点命中或异常崩溃后,查看寄存器、变量、函数调用栈,还原现场。
④运行时临时修改内存、寄存器变量的值,用于验证问题修复逻辑(修改仅在调试期间生效,不改动原始可执行文件或固件)。
(4)GDB支持的编程语言:C/C++、Go、Rust、Fortran、Assembly(汇编语言)等。
2、嵌入式远程调试架构
(1)被调试的目标程序分三类运行场景:
①本地调试:GDB直接控制和调试同一台机器上的进程。
②远程调试:GDB通过串口/TCP连接到远程目标上的GDB Server,调试远程程序。
③嵌入式/模拟器调试:GDB连接到OpenOCD/QEMU等,间接调试裸机固件或模拟器里的系统。
(2)下图所示的是嵌入式远程调试架构,本文的重点也是嵌入式软件的调试。

①Host主机层:
主机层一般是电脑主机;IDE/DDD/GDBGUI是图形调试前端,比如VSCode、CubeIDE、可视化GDB工具;GDB是核心调试客户端,主要用于下发端点、读写内存等调试指令;OpenOCD是GDB远程服务端,适配ST-Link/J-Link等调试器,把GDB指令转为SWD/JTAG硬件时序
②HW probe硬件探针层:
硬件探针层主要是硬件调试转换器,比如ST-Link,USB口连接电脑,SWD/JTAG排线连接单片机,负责电信号转换
③Target目标设备层:
在嵌入式中,被调试的目标是开发板/芯片,图中所示的是两种架构,相应的,有两种完成独立的远程调试链路
右侧链路是Cortex-M单片机主流方案,调试员在PC端图形调试界面操作,界面后端将操作指令转给GDB,GDB通过TCP连接OpenOCD(默认3333端口),向其下发调试命令,然后OpenOCD驱动ST-Link硬件,把指令转换成SWD/JTAG电平时序,通过SWD/JTAG直接与Cortex-M4的调试接口通信,实现读写寄存器、烧 Flash、启停程序
左侧链路是Cortex-A Linux MPU方案,调试员在PC端图形调试界面操作,界面后端将操作指令转给GDB,GDB通过网口TCP远程连接开发板上运行的gdbserver,gdbserver是Linux端轻量代理,接管A7内核上被调试的应用程序
二、GDB下载及安装(截至2026年,仍适用)
1、Windows下载嵌入式调试用ARM交叉GDB
(1)在官网下载页面找到Windows版本,下载.exe安装包。
官网链接:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

(2)双击.exe安装包,安装GDB。
(3)如果安装过程中未勾选“Add path to environment variable”,则需要手动添加环境变量。

2、Windows本地C/C++程序调试用GDB
(1)在官网下载页面找到Windows版本,下载.exe安装包。

(2)安装MSYS2,安装路径推荐为“C:\msys64”。

(3)安装完成后,打开MSYS2终端,执行安装命令“pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain”,回车确认全部包,然后输入“y”,等待下载安装完成。

(4)安装目录下生成路径“C:\msys64\ucrt64\bin”,也要将其添加到环境变量。

三、嵌入式ARM GDB的使用
1、GDB连接STM32
(1)OpenOCD下载:
①本节仍旧以Windows环境为例,访问“https://github.com/openocd-org/openocd”(本文撰写于2026年,链接有效),按照下图所示的指引点击,下载压缩包。


②将下载的压缩包解压到软件安装的位置(直接解压,无安装程序需要运行),并将解压后的文件下的bin文件夹路径添加到环境变量中。

③打开终端,输入“openocd -v”后能够得到如下输出。

(2)编译工程,生成.elf文件:
①这里以STM32入门教程中的“读写内部FLASH”实验为例,使用Keil打开该工程,按照下图所示的步骤进行操作。

[1]“Name of Executable”是自定义工程名,也是默认生成的.axf文件名称。这里需要补充说明的是,.axf文件是ARM扩展版.elf文件,arm-none-eabi-gdb直接支持读取.axf文件。
[2]“Debug Information”默认已勾选,编译时生成源码调试符号,否则GDB无法看到源码断点。
[3]“Browse Information”默认已勾选,编译时补充源码浏览信息,否则GDB无法浏览源码。
②然后按下图所示进行操作,能够使每个函数独立ELF段,GDB单步、断点定位更精准。

③编译工程,.axf文件默认输出在工程Objects文件夹。

④如果不是使用Keil编译工程,而是直接使用命令,那么需要在编译命令后增加“-g”参数,生成带调试符号的.elf文件。
(3)使用ST-Link将PC与STM32芯片连接,ST-Link负责转换USB/SWD信号。(也可使用J-Link,J-Link同样支持USB/SWD信号转换)
(4)启动OpenOCD:
①按下“Win+R”,输入“CMD”后按下回车,打开一个终端窗口。

②加载调试器的.cfg配置文件,以ST-Link为例,命令参数为“-f interface/stlink.cfg”,其中“interface/”是OpenOCD内置接口文件夹,stlink.cfg是ST-Link调试器的驱动配置文件,如果调试器是J-Link则换成jlink.cfg,以此类推。
③加载芯片内核的.cfg配置文件,以STM32F1系列单片机为例,命令参数为“-f target/stm32f1x.cfg”,其中“target/”是芯片内核配置文件夹,stm32f1x.cfg适配全系列STM32F1单片机,如果是其它系列的单片机,更换为相应的配置文件即可。
④已知命令参数后,在终端输入“openocd.exe -f interface/stlink.cfg -f target/stm32f1x.cfg”,如果返回以下内容,说明OpenOCD已经启动(无ERROR信息说明连接成功,下图中的警告只是声明OpenOCD选择SWD传输,无实质影响,消除方法在下面给出)。

⑤如果要消除传输协议的警告,可以自定义配置文件,比如“my_stm32f1.cfg”,用记事本编辑,文件内容如下(接口驱动和目标芯片配置文件视情况进行替换),以后启动OpenOCD时,只加载这一个配置文件即可。
# 1. 先设定传输方式(消除弃用警告)
transport select swd
# 2. 加载接口驱动
source [find interface/stlink-v2.cfg]
# 3. 加载目标芯片配置
source [find target/stm32f1x.cfg]
# 4. 可选:连接后自动复位并暂停
init
reset halt
(5)打开嵌入式GDB,连接STM32:
①按下“Win+R”,输入“CMD”后按下回车,打开第二个终端窗口。

②使用cd命令将当前所在目录切换到固件文件所在目录(Keil生成的.axf文件默认在工程Objects文件夹)。

③启动交叉GDB,加载固件符号文件,具体操作为运行命令“arm-none-eabi-gdb <固件符号文件>”,其中固件符号文件就是.elf/.axf文件。(Keil生成的.axf是ARM扩展ELF,GNU GDB对Keil专属内存段RW_IRAM1识别不完整,所以会有一条相关警告,不影响调试)

④运行命令“target remote localhost:3333”,TCP连接OpenOCD 3333端口,无报错、无红色提示文字说明连接成功。

(6)执行初始化指令:
①运行命令“load”,将.elf/.axf固件下载到单片机Flash。“load”执行成功,固件下载到Flash,会打印内存段 RW_IRAM1 / ER_IROM1、下载速度、总长度。

②运行命令“monitor reset halt”,复位单片机,并停机在复位入口。“monitor reset halt”正常停机,终端输出内核状态——xPSR、pc程序计数器、msp主栈指针。

③运行命令“b main”,在main函数入口设置断点。下图所示“b main”断点创建成功,分配硬件断点地址 0x8000b08。

④运行命令“c”,运行程序,触发断点自动停下。

2、嵌入式ARM GDB(arm-none-eabi-gdb)全套常用命令
(1)连接/芯片控制命令:通过“monitor xxx”下发指令给OpenOCD底层。
|
指令 |
执行动作 |
|
monitor reset halt |
复位单片机,使程序停止在复位入口 |
|
monitor reset run |
复位后直接运行程序 |
|
monitor flash erase_sector 0 0 7 |
擦除Flash 0~7号扇区 |
|
monitor shutdown |
关闭OpenOCD调试服务 |
|
monitor version |
查看OpenOCD版本、调试探针信息 |
|
monitor rtt poll |
RT-Thread RTT实时打印 |
|
monitor rtt stop |
停止RT-Thread RTT实时打印 |
(2)断点控制命令:在Flash中的程序打断点。
|
指令 |
执行动作 |
|
b <函数名> |
在指定函数的入口打断点 |
|
b <源文件名>:<指定行号> |
在指定源文件的指定行打断点 |
|
b <函数名> if <变量名>==10 |
在指定函数的入口打条件断点,只有指定变量等于指定值时才触发(仅支持全局/静态变量) |
|
info breakpoints / info b |
查看所有断点编号 |
|
delete <断点编号> / d <断点编号> |
删除指定断点,如果不指定编号则清空全部断点 |
(3)设置硬件观察点:监控内存读写,也即监控RAM中的变量。
|
指令 |
执行动作 |
|
watch g_val |
写观察点,设定变量g_val被修改时自动停机 |
|
rwatch g_val |
读观察点,设定读取变量g_val就停机 |
|
awatch g_val |
设定变量g_val被读访问或写访问都停机 |
(4)程序运行与单步执行:控制程序的执行动作,常用于定位问题点。
|
指令 |
执行动作 |
|
c / continue |
继续运行,直到下一个断点 |
|
s / step |
单步运行,进入函数(会跳进子函数) |
|
n / next |
单步运行,跳过函数(不进入子函数) |
|
fin / finish |
执行完当前函数,跳出到上层调用处 |
|
until <指定行> |
直接运行到当前文件指定行停下 |
(5)查看变量、内存、寄存器:
①变量查看:
|
指令 |
执行动作 |
|
p g_uart_status |
打印全局变量g_uart_status的值 |
|
p &var |
打印变量var的地址 |
|
p/x g_buffer[<元素下标>] |
十六进制打印数组g_buffer的指定元素 |
|
p/t cnt |
二进制打印变量cnt的值 |
|
display g_cnt |
每次单步自动打印变量g_cnt(自动刷新) |
|
undisplay |
取消自动打印 |
②寄存器查看(内核通用——R0~R15、PC、SP、LR、xPSR):
|
指令 |
执行动作 |
|
info registers / info reg |
打印全部内核寄存器 |
|
p $pc |
单独打印程序计数器 PC(当前运行地址) |
|
p $sp |
打印栈指针SP |
③内存读写(查看外设寄存器、RAM/Flash):
|
指令 |
执行动作 |
|
x/10w <起始地址> |
从起始地址读取10个32位字,十六进制显示(“x/20w $sp”可用于查看栈内容,判断栈溢出,“$”用于标识内核寄存器) |
|
x/20h <起始地址> |
从起始地址读取20个16位半字,十六进制显示 |
|
set {<数据类型>}<内存地址> = <修改值> |
手动修改内存地址的值(只读部分无法修改),修改位数取决于数据类型 |
(6)堆栈/崩溃定位:
|
指令 |
执行动作 |
|
bt / backtrace |
打印函数调用栈(发生HardFault必用,定位崩溃点) |
|
bt full |
打印完整栈,附带每层局部变量 |
|
frame <栈帧号> |
切换到指定栈帧,查看该层局部变量 |
|
info locals |
打印当前函数所有局部变量 |
(7)源码与反汇编调试:
|
指令 |
执行动作 |
|
l / list |
打印当前位置前后10行源码 |
|
l <文件名>:<指定行> |
直接查看指定文件行 |
|
disassemble / disas |
打印当前函数汇编代码 |
|
disas <函数名> |
反汇编指定函数(“disas $pc”可用于查看崩溃处汇编,“$”用于标识内核寄存器) |
(8)固件烧录与文件操作:
|
指令 |
执行动作 |
|
load |
将elf固件下载到单片机Flash(连接OpenOCD后执行) |
|
file new.elf |
切换加载另一个固件符号文件 |
|
compare-sections |
校验Flash下载是否和elf一致 |
(9)退出调试:
|
指令 |
执行动作 |
|
q / quit |
退出GDB客户端 |
(10)日志:
|
指令 |
执行动作 |
|
set logging file gdb_log.txt |
指定日志保存文件名,如果没有则在当前CMD工作目录生成 |
|
set logging on |
启用日志写入,开启后,所有后续命令输出(寄存器、内存、断点、反汇编、打印变量)全部写入指定的日志保存文件 |
|
set logging off |
关闭日志 |
|
set logging overwrite on |
每次新日志覆盖旧文件(默认为追加写入) |
|
set logging redirect off |
把输入终端的指令也写入日志 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)