一、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

把输入终端的指令也写入日志

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐