Windows 下 STM32MP157‑M4 OpenOCD/GDB 疑难杂症:No registers、10054、UsageFault、编码警告
STM32MP157 M4 核 + LiteOS-M 调试踩坑:OpenOCD + GDB Windows 环境系列问题
技术博客|面向 STM32MP157 M4 核 + LiteOS-M 移植的开发者
一句话结论:M4 跑在内部 SRAM、用 OpenOCD + GDB 在 Windows 上调试,坑几乎全在「GDB 与 OpenOCD 的协作方式」上,不在你的代码。 本文把No registers、10054、UsageFault、CP1252 编码、CFSR 排查这些高频问题一次性讲透。
摘要
STM32MP157 的 M4 内核调试,程序下载到 M4 内部 SRAM。Windows 环境下 OpenOCD + GDB 组合会遇到一堆细碎坑:新建 GDB 连接后报 No registers 无法修改 PC/SP;关闭 GDB 重连出现 10054 套接字错误;刚 attach 直接进入 UsageFault_Handler;print &Reset_Handler 出现 CP1252 编码转换警告;时而看到 ?? () 无符号,时而直接显示异常函数符号。本文记录完整现象、截图说明、根因、标准调试流程,同时补充 CFSR 寄存器排查 UsageFault 的实战方法。
💡 适用场景:STM32MP157 M4 独立运行在内部 SRAM、LiteOS-M 操作系统、GCC + Makefile 裸金属开发,不使用 Keil、不使用 STM32CubeIDE。
1、报错现象 1:set $pc = 0x1000ddf4 → No registers(含编码警告)
复现控制台完整输出
(gdb) print &Reset_Handler
warning: could not convert 'Reset_Handler' from the host encoding (CP1252) to UTF-32.
This normally should not happen, please file a bug report.
$1 = (<text variable, no debug info> *) 0x1000ddf4 <Reset_Handler>
(gdb) set $pc = 0x1000ddf4
No registers.
(gdb) set $pc = 0x1000ddf4
No registers.
根因
1)编码警告部分
warning: could not convert 'Reset_Handler' from the host encoding (CP1252) to UTF-32.
这是 Windows 版 arm-none-eabi-gdb 工具链的一个老 bug:Windows 系统默认代码页是 CP1252,GDB 在解析符号名称做编码转换时失败。
这个警告可以忽略,不影响调试,打印出来的函数地址 0x1000ddf4 是完全正确可用的。
⚠️ 坑点:不要直接写
set $pc = &Reset_Handler,继续使用符号会再次触发编码异常。直接写物理地址数字,彻底绕开符号解析问题。
2)No registers 报错
GDB 网络层面已经连上 OpenOCD 服务端,但是 M4 CPU 内核处于运行状态,没有被硬件 halt 暂停。CPU 正在运行时,GDB 无法读写内核通用寄存器、PC、SP,因此执行 set $pc、set $sp 直接返回 No registers。
⚠️ 注意:GDB 内置的
halt命令在 OpenOCD 场景下无效。必须使用monitor halt,把命令转发给 OpenOCD,由 OpenOCD 在硬件层面暂停 M4 内核。只有内核 halt 之后,才具备读写寄存器权限。
正确操作顺序
# 硬件暂停 M4 内核,拿到寄存器访问权限
monitor halt
# 读取向量表 0x10000000,获取初始栈指针 SP
x/w 0x10000000
# 填入上面 x/w 读到的栈地址
set $sp = 0xXXXXXXXX
# 使用物理地址设置程序入口,规避 Windows gdb 编码 bug
set $pc = 0x1000ddf4
# 恢复 CPU 运行
continue
2、报错现象 2:OpenOCD 窗口 WSAGetLastError==10054,远程主机强迫关闭了一个现有的连接

控制台输出:
Error: Error on socket 'GDB': WSAGetLastError==10054,message: 远程主机强迫关闭了一个现有的连接。
Info : dropped 'gdb' connection
根因
我们直接关掉 GDB 客户端 CMD 窗口,TCP 连接被强制断开,OpenOCD 作为服务端打印这条日志。
10054 不等于硬件故障!
- OpenOCD 进程仍然正常运行;
- ST-Link 连接正常;
- M4 的 SRAM 不会清空,上一次下载的程序还保留在内存里。
窗口分工(非常关键)
1)OpenOCD 窗口(服务端):尽量后台常驻,不要频繁关闭
负责 ST-Link 硬件交互。只有 ST-Link 识别失败、芯片 HardFault 锁死时才重启。出现 10054 直接无视,不需要重启 OpenOCD。
2)GDB 窗口(客户端):可以反复关闭、新建
每次新开 GDB 终端,必须重新执行连接命令,建立 TCP 会话。
连接命令选择
❌ 不推荐旧命令
target remote localhost:3334
✅ 推荐使用 extended-remote,支持多次断开重连,适配频繁开关 GDB 场景
target extended-remote localhost:3334
3、现象 3:attach 连上直接停在 UsageFault_Handler 异常
**
**
控制台现象:
0x10005a46 in UsageFault_Handler () at Core/Src/stm32mp1xx_it.c:143
143 while (1)
两种 attach 现场对比
场景 A:刚 attach,0x00000008 in ?? ()
芯片复位、SRAM 为空,还没有下载固件。GDB 读取该地址,找不到对应 elf 符号信息,显示问号 ?? ()。
场景 B:刚 attach 直接停在 UsageFault_Handler (),有完整源码行号符号
复现条件:关闭 GDB 客户端,芯片不断电、不复位。M4 的 SRAM 是易失内存,但只有掉电才会清空;关闭 GDB 不会擦除 SRAM,上一次跑崩的程序仍然留在内存。上一次运行已经触发 UsageFault 异常,死循环在 Fault_Handler;重新 attach 上来直接读到内存代码,识别到函数符号,于是停在异常处理函数。
⚠️ 高频大坑:
monitor load_image重新下载新版 elf 镜像,仅仅是把新代码写入 SRAM,CPU 的 PC/SP 寄存器不会自动跳转到 Reset_Handler。CPU 还停留在之前 Fault 死循环。
下载完成后,必须手动monitor halt,再手动设置 SP、PC,最后continue,新程序才会正常启动。
4、实战:通过 CFSR 寄存器定位 UsageFault 故障根源
UsageFault 属于 Cortex-M 内核的用法错误异常,常见诱因:栈溢出、非对齐访问、跳转到非法指令地址、执行 Thumb 模式错误、访问受保护外设地址。
进入 Fault_Handler 死循环之后,不要直接复位,读取 CFSR 寄存器可以直接定位故障类型。
操作步骤(GDB 中执行)
# 内核先暂停
monitor halt
# 打印全部内核寄存器,里面包含 CFSR 寄存器
monitor reg
在输出的一大段寄存器中找到 cfsr。CFSR 是 32 位组合故障状态寄存器,由三部分拼接(低位到高位):
MMFSR[7:0]:Memory Management Fault 状态(位 0~7)BFSR[15:8]:Bus Fault 状态(位 8~15)UFSR[31:16]:Usage Fault 状态(位 16~31)
UFSR(UsageFault)关键位说明
| 位(UFSR 内) | 宏定义 | CFSR 实际位 | 含义 |
|---|---|---|---|
| BIT0 | UNDEFINSTR | bit16 | 执行了未定义的指令 |
| BIT1 | INVSTATE | bit17 | 无效的 EPSR 状态,最常见:内核跑进 ARM 模式(Cortex-M 只支持 Thumb 指令集) |
| BIT2 | INVPC | bit18 | 异常返回时 PC 非法 |
| BIT3 | NOCP | bit19 | 访问不存在的协处理器指令 |
| BIT8 | UNALIGNED | bit24 | 非对齐内存访问触发故障 |
| BIT9 | DIVBYZERO | bit25 | 除零错误 |
举例分析
1. 如果看到 UFSR BIT1=1(即 cfsr 的 bit17,典型值 0x00020000)→ INVSTATE
根源:程序跑入 ARM 指令模式。M4 只支持 Thumb-2 指令集,一般是链接脚本入口设置错误,或函数指针跳转地址最低位不是 1。Cortex-M 函数指针地址最低 bit 必须置 1,代表 Thumb 模式。
2. 如果看到 UFSR BIT8=1(bit24)→ UNALIGNED
根源:非对齐访问,比如对
uint32_t变量做 4 字节读取,但地址不是 4 字节对齐。LiteOS-M 配置里可以关闭非对齐访问报错。
3. 如果看到 UFSR BIT0=1(bit16)→ UNDEFINSTR
根源:跑到非法代码地址,通常是栈溢出冲毁函数返回地址,跳转到随机内存,译码出非法指令。大概率任务栈配置太小,发生栈溢出。
补充:读取故障发生时的 PC 地址
monitor reg 输出里的 pc,是当前停在 Fault_Handler 的 PC,不是故障发生那一刻的 PC。发生异常时,内核会把故障现场的 xPSR、PC、LR、R0~R3 压入发生异常时的栈。如果要拿到出事那一刻的 PC,需要从 SP 指向的栈帧去回溯——这是 M4 内核调试排错很关键的技巧。
🔧 常见踩坑点:移植 LiteOS-M,任务栈大小配置过小,任务运行栈溢出,大概率直接触发 UsageFault。优先检查任务栈大小是否足够。
5、Windows OpenOCD + GDB M4 SRAM 调试完整模板(新开 GDB 直接整套复制执行)
文件名以你工程实际产物为准,本例为
build/m4_liteos.elf;若你的构建产物叫别的名字(如liteos_m.elf),替换对应路径即可。
# 加载 elf 符号信息
file build/m4_liteos.elf
# 连接 OpenOCD 服务端
target extended-remote localhost:3334
# 硬件暂停 M4 内核,必须!否则无法修改 pc/sp
monitor halt
# 下载镜像写入 M4 内部 SRAM
monitor load_image build/m4_liteos.elf
# 读取向量表,拿到初始栈指针
x/w 0x10000000
# 将上面 x/w 打印出的栈地址替换此处
set $sp = 0xXXXXXXXX
# Reset_Handler 入口物理地址,规避 Windows gdb 编码警告 bug
set $pc = 0x1000ddf4
# 设置断点示例
b main
# 启动内核运行
continue
6、全套踩坑总结
- 修改
$pc、$sp寄存器之前,必须先执行monitor halt硬件暂停 M4 内核,否则报No registers;GDB 自带halt命令无效。 - Windows GDB 打印
Reset_Handler报 CP1252 编码警告属于工具链 bug,地址输出有效;直接使用物理数字地址,不要使用符号&Reset_Handler。 - OpenOCD 打印 10054 套接字错误,只是 GDB 客户端关闭,硬件没有问题,不要盲目重启 OpenOCD 服务端。
- M4 内部 SRAM 只有掉电才清空;关闭 GDB 不会擦除内存,重连会看到上一次程序崩溃现场。
monitor load_image下载镜像不会自动复位 CPU,下载完成务必手动设置 SP 与 PC,否则 CPU 继续跑之前的异常死循环。- GDB 连接优先选用
target extended-remote,不要用老旧target remote,更适合反复断开重连调试。 - 遇到 UsageFault 不要直接复位,
monitor halt + monitor reg查看 CFSR 寄存器,根据 UFSR 位定位:非法指令、Thumb 模式错误、非对齐访问、栈溢出等问题;LiteOS-M 移植优先检查任务栈大小是否足够。
适用场景:STM32MP157 M4 独立运行在内部 SRAM、LiteOS-M 操作系统、GCC + Makefile 裸金属开发,不使用 Keil、STM32CubeIDE。
更多推荐



所有评论(0)