首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

stm32 hard fault及堆栈探究(3)

stm32 hard fault及堆栈探究(3)

(2)产生hard fault的情况。
主循环的起始部分汇编代码如下,需要在Time_Display()后的寄存器值和回退地址都弹出。

[cpp] view plaincopy

  • <span style="font-size:13px;">   196: void Time_Show(void)   
  • 0x08000D90 BD1F      POP      {r0-r4,pc}  
  • 0x08000D94 517F      STR      r7,[r7,r5]</span>  

刚进入Time_Display()时的汇编代码如下,将R0-R4,及LR都压入栈中。
[cpp] view plaincopy

  •    165: void Time_Display(uint32_t TimeVar)   
  • 0x08000D40 E8BD4010  POP      {r4,lr}  
  • 0x08000D44 F7FFBEEA  B.W      RTC_WaitForLastTask (0x08000B1C)  
  •    166: {   
  •    167:         uint32_t THH = 0, TMM = 0, TSS = 0;   
  •    168:         char buf[10];   
  •    169:         /* Reset RTC Counter when Time is 23:59:59 */
  • 0x08000D48 B51F      PUSH     {r0-r4,lr}  
  • 0x08000D4A 4604      MOV      r4,r0  

此时STM32芯片寄存器和内存的情况如下图所示。

此时buf的地址为0x200003ec,即R1的起始位置。变量和寄存器值的覆盖关系,或许是编译器检测到R1~R3的值在出栈后将不会被使用,而对内存进行的优化。此时内存中没有其他局部变量的位置,是因为在改动了代码的情况下,编译器判断为,只需在寄存器里就可以完成计算操作,因此改变了函数的汇编代码,没有占用内存空间。buf的赋值是按从低地址到高地址的顺序进行的。从内存的分配图中可以看出,如果buf越界,数组元素超过12个,就将影响到R4的内容。而如1中所述,R4的内容是Time_Display()退出后,需要读取的内存地址。如果经sprintf()后,buf内有15个字符,加上0x00,共16个字符,正好完全覆盖R4,且R4的最高位为0x00,显然是一个非法的内存空间,因此将进入hard fault。如果buf内的字符数落在(12,16)区间内,R4的地址合法(仍为0x20开头),不会进入hard fault,但地址已被修改,错误的内存空间中数值未知,程序跑飞。这些分析与实际测试结果是一致的。

问题得到了解释,也不知花了一天时间分析这些值不值。出错与否,除了程序本身的正确以外,编译器将C翻译成汇编的发挥程度也是很大的决定因素。想避免这些头疼的问题,结论就一句话:数组不要越界。
继承事业,薪火相传
返回列表