HardFault 错误信息的解释和可采用的处理方法

1.发生中断时处理器的行为

不考虑其他细节,M3内核在发生中断时首先自动将如下8个寄存器压栈。因此在中断处理函数中,发生中断时正常执行时的寄存器数值已经被压入了堆栈中。在中断处理函数开始执行时,除了PC,LR,SP等控制寄存器,从r0-r12等这些通用寄存器的数据是没有变化的。下图描述了M3内核将寄存器压栈的顺序:

地址 寄存器 被保存的顺序
旧SP(N-0) 原先已压入的内容 -
(N-4) xPSR 2
(N-8) PC 1
(N-12) LR 8
(N-16) R12 7
(N-20) R3 6
(N-24) R2 5
(N-28) R1 4
新SP(N-32) R0 3

2.编译器通过栈来实现函数调用

C编译器通过栈来实现函数的调用,即在栈中记录程序执行的轨迹并辅助寄存器进行参数传递。具体如何实现C函数的调用,历史上有很多的规范,这些规范叫做调用惯例。
对于ARM处理器来说,有一个官方的规范AAPCS(Procedure Call Standard for the ARM? Architecture)详细描述了进行函数调用时如何进行参数的传递和调用路径的记录等。
如下仅对使用栈记录调用路径的行为进行简单描述:查看编译器生成的汇编代码可以得知,大多数的函数调用通过BL语句实现,BL语句将当前程序下一条指令的地址存入LR寄存器,并跳转到指定的地方(子函数开始的地方)开始执行。子函数中如果还需要调用孙子函数,就会在函数的入口处将LR的值压栈,以便函数执行结束后能够返回父函数。因此依次找到栈中LR的数值,就能找到调用路径中各个函数的地址。最后根据map文件翻译出各函数的名称,就可以得到函数的调用路径了。
如下是一个简单函数汇编代码的例子,函数OnPowerOff调用了函数FS_Deinit,函数FS_Deinit调用了SPIFFS_unmount??梢钥闯鯫nPowerOff函数的入口如将LR压入栈中(此时LR中保存的是函数OnPowerOff的返回地址,也就是调用OnPowerOff的父函数中的某条指令的地址),然后调用了FS_Deinit。同样FS_Deinit也在入口处将LR压入栈中(此时LR中保存的是OnPowerOff函数中POP指令的地址),然后再调用SPIFFS_unmount。返回的过程,依次将栈中保存的返回地址直接出栈到PC寄存器,完成函数的返回。这样,如果某个函数将栈中的返回地址写坏,则函数在返回时就会跳转到某个随机的地方,这就是常说的“程序跑飞了”。

OnPowerOff PROC
;;;202 void OnPowerOff(void)
0001b2 b510 PUSH {r4,lr}
;;;203 {
;;;204 FS_Deinit();
0001b4 f7fffffe BL FS_Deinit
;;;205 }
0001b8 bd10 POP {r4,pc}
;;;206
ENDP
FS_Deinit PROC
;;;166 void FS_Deinit(void)
000158 b510 PUSH {r4,lr}
;;;167 {
;;;168 SPIFFS_unmount(&g_fs);
00015a 4810 LDR r0,|L1.412|
00015c f7fffffe BL SPIFFS_unmount
;;;169 return;
;;;170 }
000160 bd10 POP {r4,pc}
;;;171
ENDP

3.对信息的继续挖掘

  • 通用寄存器
    通用寄存器中可供挖掘的信息并不多,通常情况下r0-r3寄存器保存着函数的前四个参数(其余的参数在栈中保存),需要注意的是:这四个寄存器的数值仅在函数开始执行的时候是可靠的,在函数执行的过程中可能被改变。在函数返回时,寄存器r0和r1用于保存返回值(根据返回数据的大小,决定仅使用r0还是同时使用r0和r1)。同样这两个寄存器仅在子函数刚返回时数值才是可靠的。
  • 特殊功能寄存器
    特殊功能寄存器就是PC、LR和SP了。
    SP指向当前的栈顶,在知晓栈的结构时,可以根据SP访问栈中的数据。
    在中断处理函数中LR有特殊用法,其中保存了返回被中断地点的方法,而不是通常情况下的返回地址。因此在Hardfault处理函数中寄存器LR和PC的值没有太多参考意义,被处理器自动压栈的LR和PC最有用,PC记录了被中断打断前正在执行的指令地址(也是正在执行的函数地址),LR记录了被中断打断前,正在执行的函数的父函数的地址。根据这两个地址,可以找到引发Hardfault异常的函数和语句,以及其父函数(如果辅以汇编代码继续对栈的内容进行分析,则可以回溯整个调用路径)。
    而具体引发Hardfault异常的原因,可以根据下面章节介绍的SCB寄存器来查看。
  • SCB寄存器
    在M3/M4处理器标准外设中,有一个叫做SCB(System Control Block)的部分,其中有6个寄存器记录了发生Hardfault异常的原因。
    CMSIS规范中对SCB寄存器的定义:
typedef struct
{
    __I uint32_t CPUID;
    __IO uint32_t ICSR;
    __IO uint32_t VTOR;
    __IO uint32_t AIRCR;
    __IO uint32_t SCR;
    __IO uint32_t CCR;
    __IO uint8_t SHP[12];
    __IO uint32_t SHCSR;
    __IO uint32_t CFSR;  //主要关注
    __IO uint32_t HFSR; //主要关注
    __IO uit32_t DFSR;
    __IO uint32_t MMFAR; //主要关注
    __IO uint32_t BFAR; //主要关注
    __IO uint32_t AFSR;
    __I uint32_t PFR[2];
    __I uint32_t DFR;
    __I uint32_t ADR;
    __I uint32_t MMFR[4];
    __I uint32_t ISAR[5];
    uint32_t RESERVED0[5];
    __IO uint32_t CPACR;
} SCB_Type;

CFSR、HFSR、MMFAR、BFAR几个寄存器是我们需要关注的,AFSR是平台相关的暂时忽略。上述寄存器中CFSR又可以分为三个寄存器分别是:UFSR,BFSR,MFSR。上述寄存器的内存分布如下表所示:

地址 寄存器 全名 尺寸
0xE000 ED28 MFSR MemManage fault 状态寄存器 字节
0XE000 ED29 BFSR 总线 fault 状态寄存器 字节
0XE000 ED2A UFSR 用法 fault 状态寄存器 半字
0XE000 ED2C HFSR 硬 fault 状态寄存器
0XE000 ED30 DFSR 调试 fault 状态寄存器
0XE000 ED3C AFSR 辅助 fault 状态寄存器

各寄存器数据的描述如下:
MFSR 中可能出现的错误及原因:

可能的原因
MSTKERR 入栈时发生错误(异常响应序列开始时)
1.堆栈指针的值被破坏
2.堆栈容易过大,已经超出MPU允许的region范围
MUNSTKERR 出栈时发生错误(异常响应序列终止时),入栈时没有发生错误,出栈时却出错,总令人有些匪夷所思,可能的原因是
1.异常服务例程破坏的堆栈指针
2. MPU配置被异常服务例程更改
DACCVIOL 内存访问?;のダ?。这是MPU发挥作用的体现。常常是用户应用程序企图访问特权级region所致
IACCVIOL 1.内存访问?;のダ?。常常是用户应用程序企图访问特权级region。入栈的PC给出的地址,就是产生问题代码之所在
2.跳转到不可执行指令的regions
3.异常返回时,使用了无效的EXC_RETURN值
4.向量表中有无效的向量。例如,异常在向量建立之前就发生了,或者加载的是用于传统ARM内核的可执行映像

BFSR 中可能出现的错误及原因:

可能的原因
STKERR (自动)入栈期间出错
1.堆栈指针的值被破坏
2.堆栈容易太大,到达了未定义存储器的区域
3.PSP未经初始化就使用
UNSTKERR (自动)出栈器件出错。如果没有发生过STKERR,则最可能的就是异常处理器件把SP的值破坏了
IMPRECISERR 与设备传送数据的过程中发生总线错误??赡芤蛭璞肝淳跏蓟穑涸谟没Ъ斗梦柿颂厝兜纳璞福蛘叽偷氖莸ノ怀叽绮荒芪璞杆邮?。此时,有可能是LDM/STM指令造成了非精确总线fault。
PRECISERR 在数据访问期间的总线错误。通过BFAR可以获取具体的地址。发生fault的原因同上。
IBUSERR 同MFSR中的IACCVIOL

UFSR 中可能出现的错误及原因:

可能的原因
DIVBYZERO 当DIV_0_TRP置位时发生除数为零。导致此fault的指令可以从入栈的PC读取
UNALIGNED 当UNALIGN_TRP置位时发生未对齐访问。导致此fault的指令可以从入栈的PC读取
NOCP 企图执行一个协处理器指令。导致此fault的指令可以从入栈的PC读取
INVPC 1.异常返回时使用了无效的 EXC_RETURN,例如:
1)当 EXC_RETURN = 0xFFFF FFF1 时却要返回线程模式
2)当 EXC_RETURN = 0xFFFF FFF9 时却要返回 handler 模式
2.无效的异?;疃刺?,例如:
1)当前异常的活动状态已经清除了,却在此时执行异常返回。往往是因为滥用 VECTCLRACTIVE 或清除了 SHCSR 中活动状态所致
2)在还有其他异常的活动位置位时,却要返回线程模式
3.由于堆栈指针错误导致了 IPSR 的值不正确。对于 INVPC fault ,入栈的 PC 指出了该 fault 服务例程在何处抢占了其他的代码。这个问题往往是比较隐晦的程序错误造成的,欲详细调查该问题的原因,最好使用ITM的跟踪功能。
4.ICI/IT 位对当前指令无效。当LDM/STM 指令被异常打断后,在异常服务例程中又更改了入栈的 PC。结果在中断返回时,非零的 ICI 位段作用到了不是用 ICI 位段的指令上。如果是其他原因破坏了 PSR 的值,也可能导致此 fault。
INVSTATE 1.加载到 PC 中的跳转地址值是偶数(LSB=0)。通过检查入栈 PC 的值,一下子就可以查出该问题。
2.向量地址的 LSB=0,诊断方法同上。
3.入栈的 PSR 在异常处理过程之中被破坏,使得在返回时内核尝试进入 ARM 状态
UNDEFINSTR 1.使用了 CM3 不支持的指令
2.代码段中的数据被破坏
3.连接时加载了 ARM 目标码。请检查编译阶段的位置
4.指令对其的问题。例如,在使用 GNU 工具链时,忘记了 .ascii后使用 .align,就有可能导致下一条指令没有对齐

解读SCB寄存器时应首先根据HFSR寄存器判断产生Hardfault的原因,如果确认是fault上访的情况,则依次检查BFSR、UFSR和HFSR确定具体的错误原因和地址。

?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352