赞
踩
单片机主要是由内核、外设、总线、内存等部分组成,目前市场上主流的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字节,实验代码如下:
- uint32_t StackPos;
- __IO uint32_t *pStack;
- void StackTest(void);
-
-
- int main(void)
- {
- StackPos = __get_MSP(); //获取栈顶地址
- pStack = (uint32_t *)(StackPos-0X200); //计算栈的边界地址
- *pStack = 0X12345678;
- LED_Init();
- StackTest();
- while(1)
- {
- SysDelayms(100);
- }
- }
-
- void StackTest(void)
- {
- uint8_t buf[600];
- uint16_t i = 0;
- buf[i] = buf[i]; //防止编译器警告
- StackPos = __get_MSP(); //获取栈顶地址
- for(i=0; i<513; i++)
- {
- buf[i] = 0X55;
- }
- }
通过该工程的map文件,得到的栈初始地址和栈空间信息:
栈的起始地址为0X20000480,大小为0X200,仿真代码,当执行 StackPos = __get_MSP() 语句后,得到的 StackPos 的数据为 0x20000680,此时MSP指针也是指向 0X20000680 地址,所以此时 StackPos 等于栈的起始地址。
接下来通过指针的方式对该地址写入 0X12345678,仿真结果如下:
可以看到,此时 pStack 指向的地址为 0X20000480, 该地址正好是栈的边界地址,通过内存看下 0X20000480 地址处的数据也为 0X12345678:
之后,调用函数 StackTest() ,在该函数内部定义了一个大数组,我们对大数组进行赋值为0X55,然后运行看下仿真结果:
此时, pStack 指针指向的地址空间的数据变为为 0X55555555,由此可见,栈已经溢出;我们可以定期检测 pStack 指向的地址的内容,从而确定栈是否溢出。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。