当前位置:   article > 正文

二、单片机——栈溢出监测_单片机怎么看栈是否溢出

单片机怎么看栈是否溢出

一、ARM-CortexM 系列内核寄存器简介

单片机主要是由内核、外设、总线、内存等部分组成,目前市场上主流的32位单片机大多数为 ARM内核的CortexM 系列的单片机,例如STM32F103、STM32F407、GD32F103等等。单片机内核是具有计算和处理能力的中心,其内部具有几个重要的寄存器:

R0~R12: 通用寄存器,可进行数据的加载

R13:栈指针(SP),用于对栈空间的存取操作(通过PUSH和POP指令),CrortexM0、CrortexM3在不用物理地址位置上存在两个栈指针,主站指针(MSP,为上电后的默认指针,用于异常处理)和进程栈指针(PSP,主要用于操作系统线程)

R14:链接寄存器LR,用于存储子程序或者函数调用的返回地址。子程序或者函数执行完毕。存储在LR中的返回地址将被加载到程序计数器(PC)中,以便调用程序可以继续执行。当异常发生时,LR会提供一个特定值用于中断返回机制;

R15:程序计数器(PC),一般指向当前正在执行的函数地址,由于哈佛三级流水钱的结构,一般会指向当前程序执行的地址+4;

二、堆栈的概念

堆和栈都是一块特定的内存单元(在RAM中),用于存储数据

:是由程序员手动申请的内存单元,例如在单片机编程过程中使用 malloc 函数申请一定字节的空间,使用后必须手动释放,例如 free;内部地址是由低到高生长,先进先出模式;

:是由单片机内部的一块存储单元,由单片机申请和释放;一般在编写函数时,定义的局部变量、数组、指针、结构体等,都是在单片机执行到该函数时为才为这些变量申请的内存空间,当函数执行完成后,内存会被单片机释放;这些变量申请后所占用的内存都属于栈空间;内部地址是由高到低生长(大多数如此,看内核架构),先进后出模式;

栈空间的动态变化:

在程序执行时,会将一些重要的数据保存在内存中,比如全局变量。由于在调用函数时会动态的申请内存,并可能对局部变量的数据进行读写操作,当栈溢出后,可能会改写内存中的数据,导致程序跑飞或者出错。所以要时刻警惕防止程序栈溢出。

三、栈溢出检测

为防止栈溢出,要时刻对栈进行检测,如何判断栈是否溢出?只要通过一些技术手段检测栈是否超出边界其实就可以检测栈溢出,检测方法如下:

(1)、获取栈边界地址;

(2)、对栈边界处的内存写一组特定的数据;

(3)、时刻检测栈边界地址的数据是否改变,如果数据被改变,说明栈溢出。

 1、获取栈边界地址:在CreateM3 内核中,可以使用 __get_MSP() 函数获取栈当前地址;由于栈只有在定义变量的时候,地址才会生长,因此当函数进入主函数后在未定义变量之前先获取此时的栈地址,此时的栈地址就是栈的起始地址,然后通过起始地址和栈的空间大小,计算出栈边界地址;

2、在获取栈边界地址后,通过指针的方式,对该地址内存单元的进行写入特定的数据;

3、在程序运行的过程中定期检测;

本次实验使用STM32F103单片机进行实验,设置的栈空间为512字节,实验代码如下:

  1. uint32_t StackPos;
  2. __IO uint32_t *pStack;
  3. void StackTest(void);
  4. int main(void)
  5. {
  6. StackPos = __get_MSP(); //获取栈顶地址
  7. pStack = (uint32_t *)(StackPos-0X200); //计算栈的边界地址
  8. *pStack = 0X12345678;
  9. LED_Init();
  10. StackTest();
  11. while(1)
  12. {
  13. SysDelayms(100);
  14. }
  15. }
  16. void StackTest(void)
  17. {
  18. uint8_t buf[600];
  19. uint16_t i = 0;
  20. buf[i] = buf[i]; //防止编译器警告
  21. StackPos = __get_MSP(); //获取栈顶地址
  22. for(i=0; i<513; i++)
  23. {
  24. buf[i] = 0X55;
  25. }
  26. }

通过该工程的map文件,得到的栈初始地址和栈空间信息:

栈的起始地址为0X20000480,大小为0X200,仿真代码,当执行 StackPos = __get_MSP() 语句后,得到的 StackPos 的数据为 0x20000680,此时MSP指针也是指向 0X20000680 地址,所以此时 StackPos 等于栈的起始地址。

 接下来通过指针的方式对该地址写入 0X12345678,仿真结果如下:

可以看到,此时  pStack 指向的地址为 0X20000480, 该地址正好是栈的边界地址,通过内存看下 0X20000480 地址处的数据也为 0X12345678:

 之后,调用函数 StackTest() ,在该函数内部定义了一个大数组,我们对大数组进行赋值为0X55,然后运行看下仿真结果:

 此时, pStack  指针指向的地址空间的数据变为为 0X55555555,由此可见,栈已经溢出;我们可以定期检测 pStack  指向的地址的内容,从而确定栈是否溢出。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/82849
推荐阅读
相关标签
  

闽ICP备14008679号