嵌入式如何防止栈内存溢出

您所在的位置:网站首页 栈溢出的原因及解决办法 嵌入式如何防止栈内存溢出

嵌入式如何防止栈内存溢出

2023-12-23 20:23| 来源: 网络整理| 查看: 265

        今天是虎年开工的第一天,也是Aven公众号开张的第一天。Aven搞电子行业也有12年多了,搞过硬件、嵌入式软件,总觉得应该写点什么记录一下。那今天就从嵌入式软件开篇吧。

        Aven手头用的嵌入式软件是IAR,那么就以实际项目来说说如何在调试和离线运行中避免栈内存溢出。

    

        我们都知道栈的特点,其实际上是一个LIFO,也就是后进先出,具备内存回收机制。其是通过硬件机制完成这一操作的,嵌入式软件工程师无需关心。无需关心不等于不用关心,程序调试或运行中最怕的是---不知道为啥宕机了。栈内存溢出就是其中一种可怕的宕机,总耽误我们好多时间。

         闲话少说,上干货。

    1.集成开发环境自带栈溢出指示

     以IAR为例,在主菜单栏“view”中调出“breakpoint”,如图1所示:

图 1

         右键选择“New Breakpoint”,再选择“code”,弹出图2界面:

图2

       图2中显示Break At的位置,为了防止调试中出现栈溢出,可在此处填写“栈底”地址,Aven此处的栈底地址是0x20000000。设置完成,点击确定,那么在断点处就多了一个0x20000000的code断点。我们都知道栈的生长方向是“由顶而下”,当栈不够用的时候,程序强制停留在了栈底位置,明确告诉程序猿,此处的程序使用栈方法有问题,要么修改栈大小,要么调整程序使用方法。

     

       如何知道栈底在哪里,其方法有三:

打开调试窗口,在“view”菜单中选择“stack”,那么会弹出图3所示:

 图3

        在图3中,将鼠标点在CSTACK后面的红色方框处,将会显示出栈底到栈顶的范围。

       b. 查看icf分散加载文件,IAR中默认为symbol为CSTACK,可在里面查看CSTACK放置在内存的哪个位置,其大小设置是多少。需要说明的是CSTACK为默认的栈名,无特殊情况没必要更改。

      c. 对于内存较小,设置较为简单的项目,可直接在集成环境中直接查看,以IAR为例,如图4所示:

图4

    图4中点击edit,就能看到CSTACK的设置,由于Aven现有的项目利用的都是icf文件,在此就不展开了。

   2.利用函数离线检查是否有栈溢出

      第一步:初始化栈空间   

大家都知道,栈空间需要在程序运行之前准备好,这部分在启动汇编文件中完成,如下程序段1所示。

程序段1

CPSID I ; Mask interrupts LDR R0, =0xE000ED08 LDR R1, =__vector_table STR R1, [R0] LDR R2, [R1] MSR MSP, R2 LDR R0, =SystemInit BLX R0;initialize manually  cstack EXTERN init_cstack LDR R0, =init_cstack        BLX     R0 CPSIE I ; Unmask interrupts LDR R0, =__iar_program_start        BX      R0

    程序段1中的init_cstack是C函数编写的栈初始化函数,如程序段2所示。

                                      

程序段2

//初始化栈空间void init_cstack(void){ uint32_t num; uint8_t *cstack_begin = __section_end("CSTACK"); uint8_t *cstack_end = __section_begin("CSTACK"); num = cstack_begin - cstack_end; while (num) { cstack_begin = cstack_begin - 1; *cstack_begin= 0xcd; num--; }}

        程序段2给栈空间完成了赋值,Aven在这里面赋值的是0xcd,赋值效果可以在memory空间中查到,只需在main函数中随机设个断点,查看0x20000000(栈底)处的值就可。因为在main函数之前,启动汇编文件已完成对栈的初始化,如图5所示。

图5

        完成了栈的初始化,那咱们就有办法处理栈是否会溢出了。因为栈的处理是硬件完成的,用到的栈空间都被CPU改变了“赋值”,未用到的栈空间仍然是咱们初始化的值。Aven在栈初始化的时候手动赋值了栈底处的值为0xcd,只要判断此处的值未被改写,那么就说明栈空间仍有富余。判断办法如程序段3所示。

程序段3 

//检查是否有栈溢出void check_overflow_cstack(void){ uint32_t *cstack_end = __section_begin("CSTACK"); if(*cstack_end != 0xcdcdcdcd) { printf("the stack is overflow\r\n"); }}

           

        以上阐述了在调试环境和离线环境下判断栈溢出的方法,编程者只需按步骤去做即可。对于ram空间较大的CPU,程序员可尽量把栈空间放大大点,只要单一函数不是过于庞大,基本上不会有问题。对于ram空间较为紧凑的cpu,程序员加入这两种方法,在编程时可起到事半功倍的效果。

        

        好了,今天的文章就写到这里吧,咱们下期见。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3