[其他ST产品] (GCC)STM32进阶详解之栈回溯

[复制链接]
 楼主| 大鹏2365 发表于 2023-1-15 17:36 | 显示全部楼层
可以看到,根据上文的分析,LR和R4已经被压栈,此时栈顶已经变成了0x20000558,我们继续向下推进, 这次把断点打在fault_test_by_div0函数一开始的位置:
 楼主| 大鹏2365 发表于 2023-1-15 17:39 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 17:40 | 显示全部楼层
继续把断点打在出错的那一行函数,继续运行分析: 6581963c3c9e82622b.png
 楼主| 大鹏2365 发表于 2023-1-15 17:40 | 显示全部楼层
对比上面两张图可以看到,栈内数据和我们分析的一模一样,现在我们即将运行错误代码,我们知道,Cortex-M系列的MCU出现错误会跳往编号为3的中断: 9040163c3ca1bd65b7.png
 楼主| 大鹏2365 发表于 2023-1-15 17:43 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 17:44 | 显示全部楼层
这里我们找到中断表,最后可以查到跳转到了HardFault_Handler函数,我们把下个断点打在这里:
 楼主| 大鹏2365 发表于 2023-1-15 17:44 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 17:45 | 显示全部楼层
可以看到,出现错误后和我们预期一样,注意两个地方:
 楼主| 大鹏2365 发表于 2023-1-15 19:04 | 显示全部楼层
1.进入中断后,LR的值会被自动更新为特殊的EXC_RETURN,这个值的含义如下:
7843263c3ddbf92d66.png
 楼主| 大鹏2365 发表于 2023-1-15 19:08 | 显示全部楼层
也就是它告诉了我们,这个中断执行完后,我们返回时该使用何种模式,和使用哪个堆栈指针。
 楼主| 大鹏2365 发表于 2023-1-15 19:09 | 显示全部楼层
2.正因为LR因为上述原因被占用了,导致我们没有办法再直接通过LR返回到中断被调用前的位置,所以进入中断后,一部分寄存器是硬件自动入栈的:
 楼主| 大鹏2365 发表于 2023-1-15 19:10 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 19:11 | 显示全部楼层
由进入HardFault_Handler后的现场图也可以看到,压栈的内容与寄存器是一一对应的。也就是说硬件替我们保存了一部分现场,你可能会问了,为何还有一部分寄存器没有被保存,万一在进入中断前我使用过它们比如R5,中断中被破坏了,代码从中断中出来后,再回去执行不是一样会出错吗?这在上一章有讲过,函数调用原则里有分哪些寄存器是需要调用者保护,哪些寄存器是需要被调用者保护的,这里除了硬件压栈的那部分,剩余的都应该由被调用者保护。
 楼主| 大鹏2365 发表于 2023-1-15 19:13 | 显示全部楼层
这样是不是一切都明了了?思考这样一个问题:

如果此时让你在HardFault_Handler中写一个函数,用它去寻找是由哪条指令执行后,导致进入了HardFault_Handler,而那个指令所在的函数又是被哪里所调用,你会写吗?
 楼主| 大鹏2365 发表于 2023-1-15 19:15 | 显示全部楼层
一切一切的关键又回到了那三个地址:
9066963c3e059612ac.png
 楼主| 大鹏2365 发表于 2023-1-15 19:16 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 19:17 | 显示全部楼层
对,就是如何从栈中找到这三个地址!

至于为什么可以打印出错误类型是除0,那很简单,即使你没有移植任何代码,在keil中HardFault_Handler打上断点,进入后,查看:
 楼主| 大鹏2365 发表于 2023-1-15 19:18 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 19:21 | 显示全部楼层
 楼主| 大鹏2365 发表于 2023-1-15 19:22 | 显示全部楼层
简单来说就是Cortex-M内核提供了一些寄存器,它们保存出错时,错误的大概类型。只需在进入错误中断后查询相关寄存器即可。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 在线客服 返回列表 返回顶部