|
昨天有位同學看了我之前的這個帖子:【STM32F303開發(fā)】+如何找到導致程序出現(xiàn)HardFault的代碼 后在社區(qū)群里說他也遇到了這個問題,但是他不太清楚那個帖子最后的那個圖片的中的有2個LR的值哪個才是有用的,該使用哪個LR的值去找跑飛的代碼。我告訴他是debug viewer中輸出的那個,他好像還是不太明白,下面我們就簡單的看下LR的變化過程。他說的那個圖片如下,其中包含了2個LR,左邊一個寄存器R14的值,右邊debug viewer輸出一個。 我們通常說的LR寄存器就是寄存器R14,它常用于在調用子程序分支時存儲返回地址,調用完成后用于返回到調用之前的地方繼續(xù)執(zhí)行下面的代碼, 《Cortex-M3 權威指南》對LR的描述如下,其中提到了LR的地址等于當前子程序分支的下一條指令的地址。 我們知道當程序發(fā)生異?;蛘咧袛鄷r會自動保存現(xiàn)場將xPSR,PC,LR,以及R0-R3由硬件自動壓入堆棧以及一些其他的處理,然后程序進入異?;蛘咧袛嗪瘮?shù),此時LR的值將會被自動被更新為特殊的EXC_RETURN.《Cortex-M3 權威指南》里的詳細的介紹如下: 下面我們根據(jù)上面的敘述結合實際的代碼看下LR的值的變化過程: 1)我們在異常函數(shù)void HardFault_Handler(void)里面設置一個斷點后debug下全速運行,這個函數(shù)是重載的系統(tǒng)的函數(shù),當出現(xiàn)HardFault時會自動進入這個函數(shù),可以看到此時的LR=0XFFFFFFF9,且此時處于Handler模式,和上面的描述相符。在這個函數(shù)里面首先判斷是使用的MSP還是SPS,然后將這個SP的值保存到r0寄存器,然后調用函數(shù)void Hard_Fault_Handler(uint32_t stack[]),此時的r0,作為函數(shù)Hard_Fault_Handler的參數(shù)。 下面我們進入到void Hard_Fault_Handler(uint32_t stack[]),具體如下,主要是打印一些信息出來,我們在下面的函數(shù)里設置一個斷點,然后進入到下面的函數(shù)中去
程序在進入函數(shù)時把r0的值傳給了r4,用于保存參數(shù)的值,因為下面要用到r0. 繼續(xù)看下面的匯編。將要輸出的字符的地址傳到r0保存,我們可以在內存中看到地址0x08000818中保存的就是要輸出的字符串'In Hard Fault Handler\n',同時也可以在我們上次得到的反匯編文件中看到這個FLASH地址保存的輸出字符。 反匯編中得到保存的輸出字符的位置,具體參考:【STM32F303開發(fā)】+使用fromelf反匯編keil生成的AXF文件 保存好參數(shù)r0后程序會跳轉到輸出函數(shù)的地址0x08002080繼續(xù)執(zhí)行,注意輸出字符串函數(shù)下面的一條語句的地址為0x080006c6,這個地址將會在程序跳轉到輸出函數(shù)時被保存在LR中,此時的LR的值將會發(fā)生變化。在0x08002080設置個斷點,然后全速運行程序,程序會暫停在輸出函數(shù)。 從下圖中可以看到LR的值從進入中斷函數(shù)時的0xFFFFFFF9變成了0x080006c7(指令最低bit0必須為1),這個值正好是我們跳轉前的下一句代碼的地址0x080006c6. 綜上,進入中斷函數(shù)時的LR為特殊的EXC_RETURN,但是如果在中斷函數(shù)中有發(fā)生子程序分支調用時,LR的值就會被壓棧,然后發(fā)生變化用于存儲子程序的返回地址。而我們用于保存跑飛代碼的LR的值一直在保存著并沒有發(fā)生變化,最后在debug viewer輸出的正是我們要找的LR的值。 |
|
|