绝大多数产品开发,软件一般都会设计成 boot + app 的形式,这是方便后续软件更新,否则更新会变成一个很麻烦的事情。
网上随处可见的跳转程序大概如下:
void jump2app()
{
typedef void (*func_app_start)(void);
__disable_irq();
func_app_start app_start = (func_app_start) (*(__IO uint32_t*) (APP_START_ADDR + 4));
__set_MSP(*(__IO uint32_t*) (APP_START_ADDR));// 设置栈顶地址
app_start();
}
大多数情况下,该程序跳转正常,但当你改变了编译器优化级别时,可能直接就 hardfault 了。
此时你会莫名其妙,为什么???
从鱼鹰18年接触到 boot 知识以来,都觉得这样的跳转程序理所当然,并且也没出现过问题,直到最近修改了优化级别,发现程序直接hardfault 了,根本没跳转到 app 中,才发现了这里隐藏的 bug。
调试发现,app_start 这个函数指针变成了异常值,但鱼鹰查看 0x08040000 处的内存空间值是正常的。那只能是代码问题了。
因此结合汇编和在线调试,终于发现,执行 __set_MSP 这条语句前 app_start 的值是正常的,执行后,这个值就异常了。
再结合 C 语言关于栈、局部变量的知识,立刻就知道是因为重新设置栈顶地址,但汇编代码不变,但是从新栈位置偏移取函数地址,因此跳转失败。
如:
新栈的位置,变量的值是未知的,用它进行跳转,失败是必然的。
但是为什么大部分情况下程序没有问题呢?
这是因为跳转程序很简单,局部变量少,那么这个 app_start 局部变量编译器可能就不会从栈中分配,而直接用一个寄存器存储数据,而寄存器是不受栈顶位置影响的,自然程序能跳转了。
但我们不能让程序运行正常与否由编译器随机决定,因此我们要避免这个 bug,让程序不管在何种优化级别、函数多复杂的情况下,依然可以正常运行,因此还是有必要进行优化的。
最简单的方法,就是把这个 app_start 局部变量变成全局变量。这样变量就不会受到栈顶位置影响,自然可以避免了。
如:
void jump2app()
{
typedef void (*func_app_start)(void);
__disable_irq();
static func_app_start app_start = (func_app_start) (*(__IO uint32_t*) (APP_START_ADDR + 4));
__set_MSP(*(__IO uint32_t*) (APP_START_ADDR));// 设置栈顶地址
app_start();
}
如有更好的优化方法,欢迎留言讨论。
全部0条评论
快来发表一下你的评论吧 !