首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
死机定位_FPU栈帧与backtrace实战
📅 2026/10/29 2:00:19
✍️ 爱科研究院
👁 阅读 3,247
本文主线先吃颗定心丸别被FPU 栈帧backtrace吓到不用啃汇编——本篇只讲三件事栈帧里多压了哪截浮点数据、漏一个宏会让栈帧当场错位 72 字节导致 backtrace 全瞎、怎么用bt把真凶扒出来。全程只要看懂内核和硬件对栈帧大小认知不一致这一核心矛盾汇编细节都给你标好了浮点栈帧的比喻见下方导读。本篇主线就一句死机定位收官核心一句话——“内核和硬件对’栈帧多大’的认知必须一致否则 backtrace 全瞎”先看los_exc.S靠LR末位判浮点、决定栈帧形状再看漏一个 FPU 双宏让异常帧错位 72 字节104/176然后学用bt顺着返回地址扒出真凶最后一句restore回干净基线。下面四站全是它的展开。️ 详细拆解路线四站跟着走不迷路第一站·栈帧的秘密los_exc.S靠异常返回LR末位判断栈帧里有没有多压浮点寄存器——判错栈帧就全歪。第二站·异常帧错位FPU 双宏漏一个异常帧当场错位 72 字节无浮点 104 / 带浮点 176backtrace 整条乱码。第三站·backtrace 实战bt怎么顺着栈上返回地址一层层还原出事前谁在调谁——前提是栈帧没被 FPU 错位。第四站·收尾一句restore把工程还原回能跑的干净基线别留破坏现场。读完全文记得回来看末尾的「全文总结」它会把上面四站串成一句话。零基础导读接第 20 篇那场事故现场勘查第 20 篇讲了交警怎么到车祸现场、用三板斧查三破坏现场本篇讲最隐蔽的一段——行车记录仪黑匣子里多录了一截浮点画面读报告的人却没注意末尾标没标含浮点结果整本报告串味、名字对不上。先把 5 个对应关系记牢后面就不晕了现实比喻真名芯片里叫啥车祸后交警出的报告书HardFault/los_exc.S异常分派记录仪里多录的一截浮点画面FPU 自动压栈的浮点寄存器S0–S15即 D0–D7报告书末尾是否含浮点的标记LR寄存器末位0xFFFFFFED / 0xFFFFFFF9读错这段导致整本报告串味栈帧错位 72 字节、backtrace 乱码把记录仪调回正确模式重读restore回能跑的干净基线一、第一站·栈帧的秘密LR 末位判浮点先讲最隐蔽也最基础的一节。los_exc.S处理 HardFault 时靠异常返回时 LR 那个值决定栈帧形状——它像开关一样告诉内核栈上到底多压了没压浮点寄存器。判对了后面bt才准判错了整条调用链当场歪掉。一句话理解异常返回时 LR 那个值的末位像开关一样告诉内核「栈上多压了浮点寄存器没有」——判错栈帧就全歪。los_exc.S里有个精妙判据——异常返回时 LR 的值决定栈帧形状LR 0xFFFFFFED硬件自动压了浮点寄存器 S0-S15即 D0-D7 FPSCRFPU 开启时栈帧多出这部分浮点上下文D8-D15 是 callee-saved规范上由软件VPUSH {D8-D15}补存——但本工程源码里这句被注释实际未保存LR 0xFFFFFFF9没有浮点寄存器异常发生在 MSP 上下文走_NoFloatInMsp分支只压整数寄存器PSP 上下文无浮点的 LR 值是0xFFFFFFFD走_NoFloatInPsp栈帧就是「这次异常发生时寄存器在内存里排成的那一摞」。内核要根据它的形状才能正确地从这摞里把调用关系还原出来。内核根据这个位选择LDMFD R3!, {R4-R11}之后要不要再#VLDMIA R3!, {D8-D15}把浮点也搬进异常栈帧。语法小课堂零基础必看那两个汇编助记符是啥第 16 篇也出现过LDMFD/VLDMIA这类 ARM 汇编这里一次讲清LDM/VLDM Load Multiple一次性从栈里搬一批寄存器回来。L开头搬整数通用寄存器R4-R11V开头搬浮点寄存器D8-D15。FD/IA 寻址方式FDFull Descending满递减栈从高地址往下长IAIncrement After地址递增后取值。都是从栈里按顺序往外掏寄存器。R3!, {R4-R11}里R3!的!表示搬完顺手把 R3 自己也更新成新位置{...}列出这次要搬哪些寄存器。一句话LDMFD R3!, {R4-R11}把整数现场从栈里捞回来要不要再补一句VLDMIA R3!, {D8-D15}把浮点现场也捞回来取决于 LR 末位告诉内核栈上到底有没有浮点——捞多捞少算错栈帧就整个错位。可复用经验异常返回时LR 末位决定栈帧形状有没有浮点寄存器0xFFFFFFED含浮点、0xFFFFFFF9不含。判错 → backtrace 整条错位成乱码。这条判据的根源是 FPU 双宏第 16/17 篇下节展开。判对了栈帧对齐backtracebt把函数调用链从栈里一层层扒出来的过程才准。第一站小结los_exc.S靠LR末位0xFFFFFFED含浮点 /0xFFFFFFF9不含判断栈帧形状它是bt能不能扒准的总开关判错整条 backtrace 就错位成乱码。二、第二站·异常帧错位FPU 双宏漏一个栈帧错位 104/176上一站讲清了判据在 LR 末位。这一站讲最惨痛的后果——FPU 双宏漏一个内核和硬件对栈帧多大的认知就不一致异常帧当场错位 72 字节104/176backtrace 整条乱码。先把教学实验和真实事故两本账分清楚再逐项算这两个数字怎么来的。⚠️先澄清一个口径当年真实卡死事故是TaskContext68/204 错位铁律③见勘误篇bx r6跳飞——那是HalTskStackInit给新任务预构造的初始上下文帧los_dispatch.S里bx r6跳飞。本篇讲的104/176 是异常帧los_exc.S处理 HardFault 时硬件自动压栈 软件补压的两档大小是 backtrace 读栈时用的对照不是当年事故。两本账都受 FPU 双宏影响、都真实存在但数字不同、用途不同见本节末 ⚠️ 框。本篇用 104/176 讲清异常帧怎么读勘误篇用 68/204 讲清任务初始帧怎么跳飞。一句话理解a/b/c 是故意造的对照实验和当年真实踩的 FPU 坑不是一回事——真实坑更隐蔽、更致命。demo_hang.sh头部自己就用红字警告a/b/c 是教学对照实验不是老师当年真实事故。对比维度教学 a/b/c本篇主题异常帧错位104/176真实事故勘误篇 68/204性质单变量、可还原的演示HardFault 后 backtrace 读栈对照移植时埋下的真 bug触发demo_hang.sh故意改一处target_config.h漏__FPU_PRESENT/__FPU_USED假设同左真实发生过现象灯不亮 / 冻住可复现HardFault 后 backtrace全乱HalStartToRun里bx r6跳飞根因调度没起来 / 心跳被掐 / 中断被关异常帧按 104 字节读、硬件实际压 176 字节TaskContext68/204 字节错位铁律③哪段代码los_dispatch.S/los_timer.clos_exc.S异常分派los_dispatch.SHalStartToRun/HalPendSV本篇只讲 104/176 这本账异常帧los_exc.Sbacktrace 用真实事故 68/204 那本账在勘误篇讲TaskContextbx r6跳飞。两者都受 FPU 双宏影响但代码段不同、数字不同。异常帧深一层Cortex-M4 硬件上有 FPU你的芯片__FPU_PRESENT实际是 1异常时硬件会自动把S0-S15即 D0-D7 FPSCR压栈D8-D15 是 callee-saved规范上要软件VPUSH补存——但本工程los_exc.S里这句被注释实际不保存见本节末 ⚠️ 框。若内核以为「没 FPU」只按整数栈帧去读却不知道硬件多压了 72 字节浮点上下文——于是每一层栈帧都当场错位 72 字节。本工程里内核有没有 FPU的开关在两处SystemInit写CPACR开硬件 FPU 电源system_stm32mp1xx.ctarget_config.h双宏决定TaskContext结构体含不含浮点成员严格讲异常分支看 LR但双宏漏了会让TaskContext错位勘误篇 68/204backtrace 读栈时栈帧形状也就跟着错。栈帧错位有多坑想象一张「通讯录」本来每页固定 10 行你却按 5 行一页去翻——翻到的全是错页、名字对不上。FPU 双宏漏了内核对栈帧「每页多少字节」的算账就错 104/176 字节backtrace 扒出来的函数名整条错位成乱码你根本定位不到真凶。图注只要开了浮点硬件一进中断就自动往栈里多塞一格浮点行李S0~S15 / D0~D7。这格卡在普通寄存器现场和你自己的局部变量中间。调试器要是没被告知这里有浮点区数格子就会数错backtrace 就糊了。结果栈帧偏移算错 104 / 176 字节backtrace 整条链错位你看到的函数名全是乱码根本定位不到真凶。104 / 176 这两个数字到底是哪来的按本工程los_exc.S源码逐项算无浮点帧 104 字节硬件自动压R0-R3 / R12 / LR / PC / xPSR共 8 字32B随后los_exc.S软件补压——保存任务 SP4BPUSH {R4-R12}9 个寄存器36B 任务R4-R11副本32B软件合计 72B。32 72 104。带浮点帧 176 字节硬件发现开了 FPU多压S0-S15即 D0-D764B FPSCR4B 对齐填充硬件帧从 32B 涨到104B源码判据就是那句ADD R12, R3, #104软件部分两分支完全一样72B。104 72 176。一个反直觉细节D8-D15 不在硬件帧里本工程也没软件保存。硬件自动压的浮点只有 S0-S15即 D0-D7 FPSCRD8-D15 是 callee-saved规范上要软件VPUSH {D8-D15}补存——但本工程los_exc.S里这句被注释掉了#VPUSHPSP 分支的VLDMIA/VSTMDB {D8-D15}同样被注释。所以两张表的实际差值只有 72B全部来自硬件多压的 S0-S15 FPSCR。这是 LiteOS-M 的取舍内核代码不碰浮点赌 D8-D15 在异常前后不会被弄脏。⚠️别把「异常帧」和「任务初始帧」两本账搞混。本篇的 104/176 是异常帧硬件自动压栈 los_exc.S 软件补压而切换汇编系列里那个著名的68/204是TaskContext结构体的尺寸HalTskStackInit给新任务预构造的初始上下文帧204 S16-S31 64B R4-R11PriMask 36B R0-R3R12/LR/PC/xPSR 32B S0-S15 64B FPSCRNO_NAME 8B非 FPU 砍掉浮点成员后 68。两本账都真实存在、都受 FPU 双宏影响、错了都会 HardFault但数字不同、用途不同68/204 管任务第一次上场时栈里摆什么104/176 管任务被打断时硬件往栈里压什么。内核若以为没 FPU漏了双宏就按 104 字节的无浮点帧去读可硬件实际按176 字节压了——于是每一层栈帧都当场错位 72 字节backtrace 顺着错位的位置往后扒整条调用链全歪成乱码。这正是配置即契约最惨痛的一课你少填两个宏硬件和内核对栈帧多大的认知就不一致了。图注这张图是全篇的坑王——开了 FPU栈帧就从 104 字节变成 176 字节多出的 72 字节浮点区如果没算准backtrace 就会读错位、整条调用链全瞎。下面教你怎么对齐。一句话记住第 16/17 篇那两个宏为什么不能省__FPU_PRESENT/__FPU_USED漏一个 → 内核误判栈帧 → HardFault 后bt全是瞎的。可复用经验__FPU_PRESENT/__FPU_USED漏一个 → 内核误判「没 FPU」走整型栈帧分支但硬件实际多压了浮点寄存器 →栈帧错位 104/176 字节 → HardFault 后bt全瞎。这两个宏必须与芯片真实 FPU 情况严丝合缝是「配置即契约」最惨痛的一课。第二站小结FPU 双宏漏一个 → 内核按 104 字节整数帧去读、硬件却压了 176 字节 → 每层错位 72Bbacktrace 整条乱码异常帧 104/176别和任务初始帧 68/204 那本账混。两个宏必须与硬件严丝合缝。三、第三站·backtrace 实战顺着返回地址把真凶扒出来前两站把为什么栈帧会错位讲透了。这一站回到第 20 篇的三板斧看bt怎么把真相扒出来——但前提是栈帧没被 FPU 错位一旦bt出来是乱码第一反应不是改业务而是先怀疑双宏。一句话理解bt顺栈上的返回地址一层层还原出事前谁在调谁——但前提是栈帧没被 FPU 错位。回到第 20 篇的三板斧当你bt出来的是正常调用链而不是乱码时就说明栈帧判对了。顺着链往回找最顶上是死机那一刻的 PC出事那一行往下一帧是调用者LR 指向的返回地址一层层剥直到找到是哪个任务、哪行代码触发了 fault。Backtrace / 调用栈 是什么它干的事一句话顺着栈上的返回地址一层层还原出事前到底是谁调了谁——就像顺着脚印往回找看车祸前这辆车都经过哪些路口。只要栈帧没错位FPU 双宏齐全这条链就准。图注backtrace 干的事像倒带看监控从死机那一帧出发顺着返回地址一层层往回捋把谁调了谁的调用链还原出来。链最顶上那个函数往往就是埋雷的地方。若bt出来是乱码第一反应是先怀疑 FPU 双宏漏了铁律③而不是去改业务代码——这条bt不准时先怀疑 FPU 双宏的经验源头正是第 20 篇结语先点出的104/176 错位导致 backtrace 全瞎本篇给出判据。第三站小结bt顺栈上返回地址一层层还原谁调了谁链准的前提是栈帧没被 FPU 错位。一旦bt乱码第一反应先怀疑__FPU_PRESENT/__FPU_USED漏了铁律③别急着改业务。四、第四站·收尾restore 一下回到能跑的基线演示和排查都看完了最后一步很重要——把工程还原回那个能跑的干净基线别把破坏现场留在工程里。一句话理解看完演示一句restore把工程还原回那个能跑的干净基线别留着破坏现场。bashtools/demo_hang.sh restore它会把main.c/app_task.c/los_dispatch.S从.demo_hang_bak/快照拷回工程回到完整四步法的干净基线——也就是两灯错相闪烁的能跑状态。可复用经验排查 / 演示完一句bash tools/demo_hang.sh restore把工程还原回完整四步法的干净基线——别让破坏现场残留。动任何「带切换脚本」的工程先还原再读、排查完再还原是两条基本纪律。第四站小结排查 / 演示完一句bash tools/demo_hang.sh restore把工程还原回干净基线——别让破坏现场残留是动带切换脚本工程的基本纪律。五、全文总结回到主线本文一句话总结建议收藏死机定位收官——__FPU_PRESENT/__FPU_USED漏一个 → 内核误判没 FPU走整型栈帧可硬件实际多压了 S0-S15 FPSCR栈帧当场错位 72 字节无浮点 104 / 带浮点 176→ HardFault 后bt整条乱码。判据在LR末位0xFFFFFFED含浮点、0xFFFFFFF9不含bt乱码先怀疑 FPU 双宏别急着改业务。扒完真凶一句restore回干净基线。用四站回顾一遍第一站·栈帧的秘密los_exc.S靠LR末位0xFFFFFFED/0xFFFFFFF9判栈帧含不含浮点判错 → backtrace 整条错位。第二站·异常帧错位FPU 双宏漏一个 → 异常帧错位 72 字节104/176bt全瞎——配置即契约最惨痛一课。第三站·backtrace 实战bt顺栈上返回地址一层层还原谁调了谁前提是栈帧没错位乱码先怀疑 FPU 双宏。第四站·收尾一句bash tools/demo_hang.sh restore回干净基线别留破坏现场。死机定位两篇收尾移植工程的「写出来 → 跑起来 → 挂了能捞」闭环就完整了第 20 篇三破坏现场 A/B/C 单变量对照、OpenOCDGDB 三板斧、los_exc.S接管 HardFault 收口成调用链本篇最隐蔽的FPU 栈帧 104/176 错位——__FPU_PRESENT/__FPU_USED漏一个 → 内核误判栈帧 → HardFault 后bt全瞎判据在 LR 末位bt乱码先怀疑 FPU 双宏。本篇可复用经验速记照着做别只读□bt乱码先怀疑 FPU 双宏查__FPU_PRESENT/__FPU_USED是否成套铁律③别急着改业务。□ 看 LR 末位0xFFFFFFED含浮点 /0xFFFFFFF9不含——判错栈帧就错位 72 字节104/176。□ 还原基线排查完一句demo_hang.sh restore回干净现场别留破坏态。术语表本文出现缩写/符号含义backtrace /bt调用栈回溯把函数调用链从栈里扒出来栈帧stack frame异常发生时寄存器在内存里排成的那一摞内核据此还原调用关系LR 0xFFFFFFED异常返回值含浮点寄存器栈帧多浮点上下文LR 0xFFFFFFF9异常返回值ThreadMSP走_ExcInMSP→_NoFloatInMspPSP 无浮点是0xFFFFFFFD_NoFloatInPsplos_exc.S 分支没浮点时按整数栈帧处理__FPU_PRESENT/__FPU_USED宏芯片是否带/启用 FPU漏了导致栈帧错位D8-D15FPU 的 64 位 callee-saved 浮点寄存器不在硬件帧里本工程 VPUSH 被注释未保存FPSCR浮点状态控制寄存器异常时一并压栈104 / 176 字节无浮点 / 带浮点异常栈帧本工程实测口径硬件 32/104 软件 72差值 72 字节HalExcHardFaultlos_exc.S 的强符号HardFault 处理入口demo_hang.sh / restore演示脚本 / 还原基线命令.demo_hang_bak/基线快照目录铁律③移植四铁律之一FPU 双宏成套漏一个 → 栈帧错位配置即契约配置项必须与硬件真实情况一致否则内核与硬件认知冲突系列导航上一篇第 20 篇 · 死机定位los_exc 与三破坏现场 本篇第 21 篇 · 死机定位FPU 栈帧与 backtrace 下一篇第 22 篇 · 移植总 Checklist八环节与四铁律板级适配与应用 07–2507 工程全景 08 BSP 09 SystemInit 10 lds上 11 lds下·解剖 12 lds下·照妖镜 13 启动文件 14 接管中断 15 target_config上 16 FPU栈档 17 改错速查 18 LED详解 19 LED实战 20 los_exc 21 FPU栈帧 22 八环节 23 全文件对账 24 六类判据 25 经验增删改留基础 01–0601 环境准备 02 源码下载裁剪 03 os_config 04 Makefile 05 链接脚本 06 烧录切换汇编 01–06汇编1 汇编2 汇编3 汇编4 汇编5 汇编6代码仓库stm32mp157-liteos-m Gitee · 基线 v0.1-baseline · 全部版本标签ee.com/tstcoder/stm32mp157-liteos-m) · 基线 v0.1-baseline · 全部版本标签
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/29 1:55:19
5 分钟跑通 LinkSwift:9 家网盘直链推给 IDM / Aria2 / cURL 的完整安装指南
2026/10/29 1:55:19
pandas 数据分析实战:从 Python for Data Analysis 的 notebook 到真实数据的完整学习路径
2026/10/29 1:55:19
用大语言模型与提示工程挖掘中证1000选股因子
2026/10/29 3:10:23
数组相邻区间按位与操作的最小次数算法
2026/10/29 3:10:23
论文AI检测降重实战:3个案例与技巧
2026/10/29 3:10:23
LeetCode盛水容器问题:双指针算法解析与优化
2026/10/29 3:10:23
数组按位与操作的最小次数算法解析
2026/10/29 3:10:23
IEEE 802.15.4-2020:低功耗无线通信的物理层与MAC层标准解析
2026/10/29 3:05:22
CLR启动失败问题排查与解决方案
2026/10/29 0:00:09
服务器巡检报告模板怎么写:从检查项到PowerShell数据回填
2026/10/29 0:00:09
Zed编辑器Split Diff功能深度评测与优化指南
2026/10/29 0:00:09
OpenAI新模型定价解析:基础档便宜八成,快档贵六倍值不值?
2026/10/28 12:27:10
全国计算机二级C语言真题PDF高效刷题指南
2026/10/28 15:37:16
requests+BeautifulSoup:爬虫抓取h1标题的入门实战指南
2026/10/28 12:37:46
递推最小二乘参数辨识MATLAB编程:二阶系统与渐消记忆实现
2026/10/28 15:37:16
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/28 15:37:15
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/28 15:37:15
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)