HardFault 錯誤信息的解釋和可采用的處理方法

1.發(fā)生中斷時處理器的行為

不考慮其他細節(jié)八孝,M3內(nèi)核在發(fā)生中斷時首先自動將如下8個寄存器壓棧。因此在中斷處理函數(shù)中,發(fā)生中斷時正常執(zhí)行時的寄存器數(shù)值已經(jīng)被壓入了堆棧中丧叽。在中斷處理函數(shù)開始執(zhí)行時,除了PC公你,LR踊淳,SP等控制寄存器,從r0-r12等這些通用寄存器的數(shù)據(jù)是沒有變化的陕靠。下圖描述了M3內(nèi)核將寄存器壓棧的順序:

地址 寄存器 被保存的順序
舊SP(N-0) 原先已壓入的內(nèi)容 -
(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.編譯器通過棧來實現(xiàn)函數(shù)調(diào)用

C編譯器通過棧來實現(xiàn)函數(shù)的調(diào)用迂尝,即在棧中記錄程序執(zhí)行的軌跡并輔助寄存器進行參數(shù)傳遞。具體如何實現(xiàn)C函數(shù)的調(diào)用剪芥,歷史上有很多的規(guī)范垄开,這些規(guī)范叫做調(diào)用慣例。
對于ARM處理器來說税肪,有一個官方的規(guī)范AAPCS(Procedure Call Standard for the ARM? Architecture)詳細描述了進行函數(shù)調(diào)用時如何進行參數(shù)的傳遞和調(diào)用路徑的記錄等溉躲。
如下僅對使用棧記錄調(diào)用路徑的行為進行簡單描述:查看編譯器生成的匯編代碼可以得知,大多數(shù)的函數(shù)調(diào)用通過BL語句實現(xiàn)益兄,BL語句將當前程序下一條指令的地址存入LR寄存器锻梳,并跳轉到指定的地方(子函數(shù)開始的地方)開始執(zhí)行。子函數(shù)中如果還需要調(diào)用孫子函數(shù)净捅,就會在函數(shù)的入口處將LR的值壓棧疑枯,以便函數(shù)執(zhí)行結束后能夠返回父函數(shù)。因此依次找到棧中LR的數(shù)值蛔六,就能找到調(diào)用路徑中各個函數(shù)的地址荆永。最后根據(jù)map文件翻譯出各函數(shù)的名稱,就可以得到函數(shù)的調(diào)用路徑了国章。
如下是一個簡單函數(shù)匯編代碼的例子屁魏,函數(shù)OnPowerOff調(diào)用了函數(shù)FS_Deinit,函數(shù)FS_Deinit調(diào)用了SPIFFS_unmount捉腥∶テ矗可以看出OnPowerOff函數(shù)的入口如將LR壓入棧中(此時LR中保存的是函數(shù)OnPowerOff的返回地址,也就是調(diào)用OnPowerOff的父函數(shù)中的某條指令的地址),然后調(diào)用了FS_Deinit桃漾。同樣FS_Deinit也在入口處將LR壓入棧中(此時LR中保存的是OnPowerOff函數(shù)中POP指令的地址)坏匪,然后再調(diào)用SPIFFS_unmount。返回的過程撬统,依次將棧中保存的返回地址直接出棧到PC寄存器适滓,完成函數(shù)的返回。這樣恋追,如果某個函數(shù)將棧中的返回地址寫壞凭迹,則函數(shù)在返回時就會跳轉到某個隨機的地方,這就是常說的“程序跑飛了”苦囱。

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.對信息的繼續(xù)挖掘

  • 通用寄存器
    通用寄存器中可供挖掘的信息并不多嗅绸,通常情況下r0-r3寄存器保存著函數(shù)的前四個參數(shù)(其余的參數(shù)在棧中保存),需要注意的是:這四個寄存器的數(shù)值僅在函數(shù)開始執(zhí)行的時候是可靠的撕彤,在函數(shù)執(zhí)行的過程中可能被改變鱼鸠。在函數(shù)返回時,寄存器r0和r1用于保存返回值(根據(jù)返回數(shù)據(jù)的大小羹铅,決定僅使用r0還是同時使用r0和r1)蚀狰。同樣這兩個寄存器僅在子函數(shù)剛返回時數(shù)值才是可靠的。
  • 特殊功能寄存器
    特殊功能寄存器就是PC职员、LR和SP了麻蹋。
    SP指向當前的棧頂,在知曉棧的結構時焊切,可以根據(jù)SP訪問棧中的數(shù)據(jù)扮授。
    在中斷處理函數(shù)中LR有特殊用法,其中保存了返回被中斷地點的方法蛛蒙,而不是通常情況下的返回地址糙箍。因此在Hardfault處理函數(shù)中寄存器LR和PC的值沒有太多參考意義渤愁,被處理器自動壓棧的LR和PC最有用牵祟,PC記錄了被中斷打斷前正在執(zhí)行的指令地址(也是正在執(zhí)行的函數(shù)地址),LR記錄了被中斷打斷前抖格,正在執(zhí)行的函數(shù)的父函數(shù)的地址诺苹。根據(jù)這兩個地址,可以找到引發(fā)Hardfault異常的函數(shù)和語句雹拄,以及其父函數(shù)(如果輔以匯編代碼繼續(xù)對棧的內(nèi)容進行分析收奔,則可以回溯整個調(diào)用路徑)。
    而具體引發(fā)Hardfault異常的原因滓玖,可以根據(jù)下面章節(jié)介紹的SCB寄存器來查看坪哄。
  • SCB寄存器
    在M3/M4處理器標準外設中,有一個叫做SCB(System Control Block)的部分,其中有6個寄存器記錄了發(fā)生Hardfault異常的原因翩肌。
    CMSIS規(guī)范中對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。上述寄存器的內(nèi)存分布如下表所示:

地址 寄存器 全名 尺寸
0xE000 ED28 MFSR MemManage fault 狀態(tài)寄存器 字節(jié)
0XE000 ED29 BFSR 總線 fault 狀態(tài)寄存器 字節(jié)
0XE000 ED2A UFSR 用法 fault 狀態(tài)寄存器 半字
0XE000 ED2C HFSR 硬 fault 狀態(tài)寄存器
0XE000 ED30 DFSR 調(diào)試 fault 狀態(tài)寄存器
0XE000 ED3C AFSR 輔助 fault 狀態(tài)寄存器

各寄存器數(shù)據(jù)的描述如下:
MFSR 中可能出現(xiàn)的錯誤及原因:

可能的原因
MSTKERR 入棧時發(fā)生錯誤(異常響應序列開始時)
1.堆棧指針的值被破壞
2.堆棧容易過大站玄,已經(jīng)超出MPU允許的region范圍
MUNSTKERR 出棧時發(fā)生錯誤(異常響應序列終止時)枚驻,入棧時沒有發(fā)生錯誤,出棧時卻出錯蜒什,總令人有些匪夷所思测秸,可能的原因是
1.異常服務例程破壞的堆棧指針
2. MPU配置被異常服務例程更改
DACCVIOL 內(nèi)存訪問保護違例。這是MPU發(fā)揮作用的體現(xiàn)灾常。常常是用戶應用程序企圖訪問特權級region所致
IACCVIOL 1.內(nèi)存訪問保護違例霎冯。常常是用戶應用程序企圖訪問特權級region。入棧的PC給出的地址钞瀑,就是產(chǎn)生問題代碼之所在
2.跳轉到不可執(zhí)行指令的regions
3.異常返回時沈撞,使用了無效的EXC_RETURN值
4.向量表中有無效的向量。例如雕什,異常在向量建立之前就發(fā)生了缠俺,或者加載的是用于傳統(tǒng)ARM內(nèi)核的可執(zhí)行映像

BFSR 中可能出現(xiàn)的錯誤及原因:

可能的原因
STKERR (自動)入棧期間出錯
1.堆棧指針的值被破壞
2.堆棧容易太大,到達了未定義存儲器的區(qū)域
3.PSP未經(jīng)初始化就使用
UNSTKERR (自動)出棧器件出錯贷岸。如果沒有發(fā)生過STKERR壹士,則最可能的就是異常處理器件把SP的值破壞了
IMPRECISERR 與設備傳送數(shù)據(jù)的過程中發(fā)生總線錯誤〕ゾ可能因為設備未經(jīng)初始化而引起:在用戶級訪問了特權級的設備躏救,或者傳送的數(shù)據(jù)單位尺寸不能為設備所接受。此時螟蒸,有可能是LDM/STM指令造成了非精確總線fault盒使。
PRECISERR 在數(shù)據(jù)訪問期間的總線錯誤。通過BFAR可以獲取具體的地址七嫌。發(fā)生fault的原因同上少办。
IBUSERR 同MFSR中的IACCVIOL

UFSR 中可能出現(xiàn)的錯誤及原因:

可能的原因
DIVBYZERO 當DIV_0_TRP置位時發(fā)生除數(shù)為零。導致此fault的指令可以從入棧的PC讀取
UNALIGNED 當UNALIGN_TRP置位時發(fā)生未對齊訪問诵原。導致此fault的指令可以從入棧的PC讀取
NOCP 企圖執(zhí)行一個協(xié)處理器指令英妓。導致此fault的指令可以從入棧的PC讀取
INVPC 1.異常返回時使用了無效的 EXC_RETURN挽放,例如:
1)當 EXC_RETURN = 0xFFFF FFF1 時卻要返回線程模式
2)當 EXC_RETURN = 0xFFFF FFF9 時卻要返回 handler 模式
2.無效的異常活動狀態(tài)蔓纠,例如:
1)當前異常的活動狀態(tài)已經(jīng)清除了骂维,卻在此時執(zhí)行異常返回。往往是因為濫用 VECTCLRACTIVE 或清除了 SHCSR 中活動狀態(tài)所致
2)在還有其他異常的活動位置位時贺纲,卻要返回線程模式
3.由于堆棧指針錯誤導致了 IPSR 的值不正確航闺。對于 INVPC fault ,入棧的 PC 指出了該 fault 服務例程在何處搶占了其他的代碼猴誊。這個問題往往是比較隱晦的程序錯誤造成的潦刃,欲詳細調(diào)查該問題的原因,最好使用ITM的跟蹤功能懈叹。
4.ICI/IT 位對當前指令無效乖杠。當LDM/STM 指令被異常打斷后,在異常服務例程中又更改了入棧的 PC澄成。結果在中斷返回時胧洒,非零的 ICI 位段作用到了不是用 ICI 位段的指令上。如果是其他原因破壞了 PSR 的值墨状,也可能導致此 fault卫漫。
INVSTATE 1.加載到 PC 中的跳轉地址值是偶數(shù)(LSB=0)。通過檢查入棧 PC 的值肾砂,一下子就可以查出該問題列赎。
2.向量地址的 LSB=0,診斷方法同上镐确。
3.入棧的 PSR 在異常處理過程之中被破壞包吝,使得在返回時內(nèi)核嘗試進入 ARM 狀態(tài)
UNDEFINSTR 1.使用了 CM3 不支持的指令
2.代碼段中的數(shù)據(jù)被破壞
3.連接時加載了 ARM 目標碼。請檢查編譯階段的位置
4.指令對其的問題源葫。例如诗越,在使用 GNU 工具鏈時,忘記了 .ascii后使用 .align息堂,就有可能導致下一條指令沒有對齊

解讀SCB寄存器時應首先根據(jù)HFSR寄存器判斷產(chǎn)生Hardfault的原因嚷狞,如果確認是fault上訪的情況,則依次檢查BFSR储矩、UFSR和HFSR確定具體的錯誤原因和地址感耙。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褂乍,一起剝皮案震驚了整個濱河市持隧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逃片,老刑警劉巖屡拨,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件只酥,死亡現(xiàn)場離奇詭異,居然都是意外死亡呀狼,警方通過查閱死者的電腦和手機裂允,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥艇,“玉大人绝编,你說我怎么就攤上這事∶蔡ぃ” “怎么了十饥?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祖乳。 經(jīng)常有香客問我逗堵,道長,這世上最難降的妖魔是什么眷昆? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任蜒秤,我火速辦了婚禮,結果婚禮上亚斋,老公的妹妹穿的比我還像新娘作媚。我一直安慰自己,他們只是感情好帅刊,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布掂骏。 她就那樣靜靜地躺著,像睡著了一般厚掷。 火紅的嫁衣襯著肌膚如雪弟灼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天冒黑,我揣著相機與錄音田绑,去河邊找鬼。 笑死抡爹,一個胖子當著我的面吹牛掩驱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冬竟,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼欧穴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了泵殴?” 一聲冷哼從身側響起涮帘,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎笑诅,沒想到半個月后调缨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疮鲫,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年弦叶,在試婚紗的時候發(fā)現(xiàn)自己被綠了俊犯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡伤哺,死狀恐怖燕侠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情立莉,我是刑警寧澤贬循,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站桃序,受9級特大地震影響杖虾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜媒熊,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一奇适、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芦鳍,春花似錦嚷往、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至菲宴,卻和暖如春贷祈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喝峦。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工势誊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谣蠢。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓粟耻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眉踱。 傳聞我的和親對象是個殘疾皇子挤忙,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355