Windows 下 STM32MP157‑M4 OpenOCD/GDB 疑难杂症:No registers、10054、UsageFault、编码警告
2026/8/29 20:55:32 网站建设 项目流程

技术博客|面向 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 $pcset $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 实际位含义
BIT0UNDEFINSTRbit16执行了未定义的指令
BIT1INVSTATEbit17无效的 EPSR 状态,最常见:内核跑进 ARM 模式(Cortex-M 只支持 Thumb 指令集)
BIT2INVPCbit18异常返回时 PC 非法
BIT3NOCPbit19访问不存在的协处理器指令
BIT8UNALIGNEDbit24非对齐内存访问触发故障
BIT9DIVBYZERObit25除零错误

举例分析

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、全套踩坑总结

  1. 修改$pc$sp寄存器之前,必须先执行monitor halt硬件暂停 M4 内核,否则报No registers;GDB 自带halt命令无效。
  2. Windows GDB 打印Reset_Handler报 CP1252 编码警告属于工具链 bug,地址输出有效;直接使用物理数字地址,不要使用符号&Reset_Handler
  3. OpenOCD 打印 10054 套接字错误,只是 GDB 客户端关闭,硬件没有问题,不要盲目重启 OpenOCD 服务端。
  4. M4 内部 SRAM 只有掉电才清空;关闭 GDB 不会擦除内存,重连会看到上一次程序崩溃现场。
  5. monitor load_image下载镜像不会自动复位 CPU,下载完成务必手动设置 SP 与 PC,否则 CPU 继续跑之前的异常死循环。
  6. GDB 连接优先选用target extended-remote,不要用老旧target remote,更适合反复断开重连调试。
  7. 遇到 UsageFault 不要直接复位,monitor halt + monitor reg查看 CFSR 寄存器,根据 UFSR 位定位:非法指令、Thumb 模式错误、非对齐访问、栈溢出等问题;LiteOS-M 移植优先检查任务栈大小是否足够。

适用场景:STM32MP157 M4 独立运行在内部 SRAM、LiteOS-M 操作系统、GCC + Makefile 裸金属开发,不使用 Keil、STM32CubeIDE。


需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询