问题: 该问题由某用户提出,发生在 STM32F2xx 器件上。据其工程师讲述:在其产品设计中,使用了第三方的软件库。出于某种原因,第三方不能提供软件库的源代码,只能以二进制映像的形式供其使用。在软件工程中,通过相关设置将该映像定位到某一固定的地址上,用户程序则是通过各个库函数的入口地址来调用相关的库函数。在软件调试过程中,其工程师发现,将库函数的入口地址赋给函数指针,再用函数指针进行函数调用会导致程序跑飞。使用调试器中止程序运行后,发现程序停留在Hard Fault 中断服务程序内。 调研:
通过其工程师演示整个过程,确认问题现象的确如其所述。使用调试器单步跟踪,发现其程序在执行完调用指令后,被调用函数指令未及执行即跳转到 Hard Fault 服务程序。由此可断定与被调用函数无关。核对函数调用所使用的入口地址,属于Flash 存贮器地址范围,且 4 字节边界对齐。检查软件中进行该函数调用的代码,如下:
将代码修改如下:
运行该程序,库函数被成功调用并返回正确结果。
结论:
函数指针取值非法而触发 Cortex-M 的出错保护机制,从而产生 Hard Fault 中断。
处理:
修改软件代码,如表(二)。
建议:
Cortex-M 并非一款全新设计的处理器,它属于 ARM 处理器家族中的一员,在其前辈的基础上发展而来。在继承了前几代处理器的若干优越性能之余,它也顺便继承了一些历史遗留问题。早在 ARM7 处理器时期,ARM 公司为了兼顾代码的执行效率和存贮效率,为其设计了两套指令集,即传说中的 ARM 指令集和 Thumb 指令集。这两种指令集都可以在同一款处理器上执行,但又被区别对待,处理器不能同时识别这两种指令集。由此,ARM7的运行模式被划分为 ARM 模式和 Thumb 模式,在每种模式下,只能识别一种指令集。然而,ARM7 处理器允许在运行中进行两种指令集模式的切换,以便能够对不同段落的代码分别用 ARM 指令集和 Thumb 指令集进行优化。通常,这种切换是通过调用指令 BX 实现的。在 BX 指令中,如果地址码为奇数,则切换到 Thumb 指令集,如果是偶数,则切换到 ARM 指令集。如表(三)的调用会切换到 Thumb 指令集,而表(四)的调用则换到ARM 指令集。不过,这两个调用最终跳转的目的地址是一样的,因为 ARM7 的执令存贮一定是半字对齐的,所在实际取指时会忽略地址的最低位。
处理器发展到 Cortex-M 这一代时,ARM 公司认识到了在两种指令集之间进行切换所带来的诸多不便与弊端,进而对这两套指令集做了整合,并扩展出了 Thumb2 指令集。这一指令中既有 32 位的指令,又有 16 位的指令。两种指令可以混合使用,而不必进行模式切换。所以,这一指令集兼顾了执行效率和存贮效率,同时也为软件编写提供了方便,实在是 ARM 公司良心发现之举。但是,作为 ARM7 的继承者,Cortex-M 上有着其前代的深刻的印记。比如,Thumb2 指令集从概念上讲,还属于Thumb 指令集的一种,尽管Cortex-M 已经不识别 ARM 指令了。同时,在 Cortex-M 的架构中,还保留有模式切换机制。而且,向 Thumb 指令集切换是被认可的,而向 ARM 指令集切换则被认为是一种错误,会被出错保护机制拦截,也就是进入了Hard Fault 中断服务程序。或许,这是 ARM 公司从软件兼容性方面有所考虑,才保留下来的一个机制。但在实际的应用中,这不折不扣的给软件设计者挖了个大坑……
|