使用堆栈卫士提高代码完整性

开发可靠的嵌入式软件取决于规划最坏的情况,并确保有防护措施和陷阱来处理这些情况。嵌入式软件中经常被忽略的一个领域是堆栈。堆栈是临时存储器,微控制器使用该堆栈来存储信息,例如局部变量,函数调用的返回地址,中断上下文和函数参数。

设置给定应用程序的堆栈大小完全取决于开发人员。许多编译器将提供默认值0x400字节,但这对于所有应用程序是否真的足够?确定任何给定应用程序的堆栈大小是困难的。开发人员需要确定最坏的情况,其中包括了解最大的函数调用深度,将在堆栈上定义多少个局部变量以及什至可能需要存储多少并发中断上下文。即使采用当今的技术和工具,这也不是一件容易的事。

那么开发人员做什么呢?他只需确定当天的风向,从稀薄的空气中抽出一个数字,然后将该数字作为烟囱的大小即可。最重要的是,如果堆栈溢出为其分配的内存区域,可能发生的最糟糕的事情是什么?图1显示了典型的内存映射以及堆,堆栈和全局/静态区域的布置位置。随着堆栈的增长,它将朝着内存映射的全局/静态变量区域增长。如果堆栈溢出,它将开始覆盖全局和静态变量定义!导致存储在内存中的值被破坏,并有机会对系统的行为造成严重破坏。

屏幕截图2015年3月11日下午8.16.56图1 –内存映射

可靠的系统不能使堆栈溢出。那么,什么技术可以检测到何时发生这种情况呢?最常见的技术之一是在堆栈和全局/静态变量区域之间创建保护区域。应该选择此保护区域,使其具有足够的大小,以使如果堆栈溢出,它将无法穿透下面的存储区域。一些常见的值是16和32字节,但是这些值必须基于一次可以放在堆栈上的最大对象。存储器映射设置的示例可以在图2中找到。

屏幕截图2015年3月11日下午8.17.17图2 –堆栈保护内存映射

微控制器可以通过两种方式监视保护区域。第一种是使用板载内存保护单元(MPU)(如果微控制器上可用)。在这种情况下,如果对内存的堆栈保护区域进行任何写访问,则将MPU设置为触发中断。中断触发将指示堆栈已溢出,并且可以执行保护措施以及错误日志记录。

如果没有MPU,则可以在系统初始化期间用已知的位模式填充内存的保护区域。然后可以创建任务或功能,以定期检查保护区域并将其与已知位模式进行比较。如果模式已更改,则应用程序可以强制执行中断,然后记录错误并开始纠正措施。

有两种不同的方法可用于创建大图案。第一种是使用链接器文件在堆栈保护中创建FILL区域。然后,在启动时进行C复制时将自动初始化该区域。第二种方法是通过创建指向堆栈保护的起点的指针,然后将位模式放入内存中,手动将位模式写入系统初始化。

可以说拥有适当的堆栈保护将降低嵌入式软件的效率。让应用程序定期检查防护罩是否完好无损,这可能需要函数调用,循环和取消引用指针。这种性能下降在现代微控制器上几乎不明显,应该是一个沉默点。在创建可靠的系统时,需要进行这些类型的检查,以确保系统按预期运行。在发生灾难性故障(例如,堆栈溢出)时,系统将能够检测出溢出并采取纠正措施,以防系统发生可怕的后果,甚至使系统的用户更糟。

发表评论

您的电子邮件地址不会被公开。 必需的地方已做标记 *

该网站使用Akismet减少垃圾邮件。 了解如何处理您的评论数据.