打印
[应用相关]

stm32栈空间分布分析——让你能更合理的设置栈空间,防止...

[复制链接]
495|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wakayi|  楼主 | 2021-6-4 10:14 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
stm32栈空间分布分析——让你能更合理的设置栈空间,防止栈溢出


[color=rgba(0, 0, 0, 0.749019607843137)]下面是我们要使用的测试代码,先贴出来, 为了更好地分析栈空间我们把栈空间用串口dump出来
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]void hex_dump(unsigned char *p, int len)
[color=rgba(0, 0, 0, 0.749019607843137)]{
[color=rgba(0, 0, 0, 0.749019607843137)]        uint32_t i, j;
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]        //printf("\n===============dump start====================\n");
[color=rgba(0, 0, 0, 0.749019607843137)]        printf("\r\n[");
[color=rgba(0, 0, 0, 0.749019607843137)]        for (i = 0, j = 0; i < len; i++)
[color=rgba(0, 0, 0, 0.749019607843137)]        {
[color=rgba(0, 0, 0, 0.749019607843137)]                printf("%02x ", p);
[color=rgba(0, 0, 0, 0.749019607843137)]               
[color=rgba(0, 0, 0, 0.749019607843137)]                if (j < 15)
[color=rgba(0, 0, 0, 0.749019607843137)]                {
[color=rgba(0, 0, 0, 0.749019607843137)]                        j++;
[color=rgba(0, 0, 0, 0.749019607843137)]                }
[color=rgba(0, 0, 0, 0.749019607843137)]                else
[color=rgba(0, 0, 0, 0.749019607843137)]                {
[color=rgba(0, 0, 0, 0.749019607843137)]                        j = 0;
[color=rgba(0, 0, 0, 0.749019607843137)]                        printf("\r\n");
[color=rgba(0, 0, 0, 0.749019607843137)]                }
[color=rgba(0, 0, 0, 0.749019607843137)]        }
[color=rgba(0, 0, 0, 0.749019607843137)]        printf("]\r\n");
[color=rgba(0, 0, 0, 0.749019607843137)]        //printf("\n===============dump end====================\n");
[color=rgba(0, 0, 0, 0.749019607843137)]}
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]void statck_dump()
[color=rgba(0, 0, 0, 0.749019607843137)]{
[color=rgba(0, 0, 0, 0.749019607843137)]    hex_dump((char *)(0x20000460-0x400), 0x400);
[color=rgba(0, 0, 0, 0.749019607843137)]}
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]void test_func(int v)
[color=rgba(0, 0, 0, 0.749019607843137)]{
[color=rgba(0, 0, 0, 0.749019607843137)]    printf("%d\r\n", v);
[color=rgba(0, 0, 0, 0.749019607843137)]    statck_dump();
[color=rgba(0, 0, 0, 0.749019607843137)]}
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]void func(void)
[color=rgba(0, 0, 0, 0.749019607843137)]{
[color=rgba(0, 0, 0, 0.749019607843137)]    char buf[50] = {0};
[color=rgba(0, 0, 0, 0.749019607843137)]    memset(buf, 0x22, sizeof(buf));
[color=rgba(0, 0, 0, 0.749019607843137)]    buf[0] = 0xFF;
[color=rgba(0, 0, 0, 0.749019607843137)]    statck_dump();
[color=rgba(0, 0, 0, 0.749019607843137)]}
[color=rgba(0, 0, 0, 0.749019607843137)]

[color=rgba(0, 0, 0, 0.749019607843137)]/**
[color=rgba(0, 0, 0, 0.749019607843137)]  * @brief  The application entry point.
[color=rgba(0, 0, 0, 0.749019607843137)]  * @retval int
[color=rgba(0, 0, 0, 0.749019607843137)]  */
[color=rgba(0, 0, 0, 0.749019607843137)]int main(void)
[color=rgba(0, 0, 0, 0.749019607843137)]{
[color=rgba(0, 0, 0, 0.749019607843137)]    int a = 10;
[color=rgba(0, 0, 0, 0.749019607843137)]    int b = 20;
[color=rgba(0, 0, 0, 0.749019607843137)]    int c = a + b;
[color=rgba(0, 0, 0, 0.749019607843137)]    char buf[100] = {0};
[color=rgba(0, 0, 0, 0.749019607843137)]    memset(buf, 0x55, sizeof(buf));
[color=rgba(0, 0, 0, 0.749019607843137)]    buf[0] = 1;
[color=rgba(0, 0, 0, 0.749019607843137)]    buf[1] = 2;
[color=rgba(0, 0, 0, 0.749019607843137)]    buf[2] = 3;
[color=rgba(0, 0, 0, 0.749019607843137)]
[color=rgba(0, 0, 0, 0.749019607843137)]          HAL_Init();
[color=rgba(0, 0, 0, 0.749019607843137)]          SystemClock_Config();
[color=rgba(0, 0, 0, 0.749019607843137)]          MX_GPIO_Init();
[color=rgba(0, 0, 0, 0.749019607843137)]          MX_USART1_UART_Init();
[color=rgba(0, 0, 0, 0.749019607843137)]
[color=rgba(0, 0, 0, 0.749019607843137)]          printf("<-- start -->\r\n");
[color=rgba(0, 0, 0, 0.749019607843137)]          printf("&a = %p\r\n", &a);
[color=rgba(0, 0, 0, 0.749019607843137)]          printf("&b = %p\r\n", &b);
[color=rgba(0, 0, 0, 0.749019607843137)]          printf("&c = %p\r\n", &c);
[color=rgba(0, 0, 0, 0.749019607843137)]          test_func(c);
[color=rgba(0, 0, 0, 0.749019607843137)]  
[color=rgba(0, 0, 0, 0.749019607843137)]          while (1)
[color=rgba(0, 0, 0, 0.749019607843137)]          {
[color=rgba(0, 0, 0, 0.749019607843137)]              printf("<-- run -->\r\n");
[color=rgba(0, 0, 0, 0.749019607843137)]             statck_dump();
[color=rgba(0, 0, 0, 0.749019607843137)]              func();
[color=rgba(0, 0, 0, 0.749019607843137)]              while(1);
[color=rgba(0, 0, 0, 0.749019607843137)]  }
[color=rgba(0, 0, 0, 0.749019607843137)]}




[color=rgba(0, 0, 0, 0.75)]


使用特权

评论回复
沙发
wakayi|  楼主 | 2021-6-4 10:14 | 只看该作者
1.获取栈顶的地址,栈空间大小
我们的工程是用keil建立的,栈空间的大小在启动文件中可以设置,这里我们设置的大小是0x400(1024字节)


栈顶地址我们可以在map文件中查看或者debug时查看sp寄存器:

需要注意的是stm32的栈是逆生长的,即进栈时是从高地址向低地址增长



通过debug我们可以看见SP寄存器的值是 0x20000460 这就是我们的栈顶地址,另外在仿真时我们可以看见汇编的指令地址都是 0x800xxxx,这是怎们回事呢,熟悉stm32的朋友都知道0x8000000是我们的ROM地址,0x20000000是我们的RAM地址,这是因为单片机并不像x86或者其他系统那样运行程序之前需要先把程序加载到内存中,单片机的内存空间有限,如果把程序全到加载到内存的话恐怕也不太现实,所有是直接在flash中运行的,限于成本慢一点我们也是可以接受的



使用特权

评论回复
板凳
wakayi|  楼主 | 2021-6-4 10:16 | 只看该作者
2.栈空间的分布
我们在程序开头的地方定义了三个变量和一个数组,我们先把程序跑起来到**test_func()**的地方

        int a = 10;
    int b = 20;
    int c = a + b;
    char buf[100] = {0};
    memset(buf, 0x55, sizeof(buf));
    buf[0] = 1;
    buf[1] = 2;
    buf[2] = 3;
        ...
        printf("<-- start -->\r\n");
          printf("&a = %p\r\n", &a);
          printf("&b = %p\r\n", &b);
          printf("&c = %p\r\n", &c);
          stack_dump();
          test_func(c);
          stack_dump();


在test_func() 之前我们dump了栈空间,我们看下栈空间中的内容



从上面dump中的内容我们可以看出来,前面的值分别是变量a, b ,c的值,而且从打印的地址来看也是一致的,包括buf数组中的内容

然后我们继续运行到test_func()中在dump下内容看下

void test_func(int v)
{
    printf("%d\r\n", v);
    char buf[100] = {0};
    memset(buf, 0xAA, sizeof(buf));
    stack_dump();
    printf("%d\r\n", v);
    return;
}




通过上面的dump我们可以看见在test_func()中的buf也被放在了栈中,通过分析我们可以看到函数中的局部变量都是在栈空间中开辟空间的,如果我们的栈空间过小很容易溢出,包括在调用子函数时寄存器的值也会被保存在栈空间中

使用特权

评论回复
地板
wakayi|  楼主 | 2021-6-4 10:33 | 只看该作者
接着往下看

        printf("<-- run -->\r\n");
    stack_dump();
    func();
    while(1);
---------------------
        void func(void)
        {
            char buf[100] = {0};
            memset(buf, 0x22, sizeof(buf));
            buf[0] = 0xFF;
            stack_dump();
        }



在进入func()之前我们dump了一次栈空间我们发现在test_func()中dump的AA AA 已经被覆盖掉,那是因为退出子函数时会执行pop ,子函数的栈空间会被释放掉,或者说SP会指回进子函数之前的地方,所有里面的内容就被后面的数据覆盖掉了

最后我们在函数func中定义了一个数组,并且dump了栈空间,让我们来看下栈中的数据是怎样的



使用特权

评论回复
5
wakayi|  楼主 | 2021-6-4 10:34 | 只看该作者
总结
栈其实就是我们提前在内存上分配好的一块空间,用来存储临时变量和函数调用时寄存器的值,在使用时要控制好大小,防止溢出

关于堆栈的更多细节推荐大家看《Cortex-M3权威指南(中文).pdf》

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

4084

帖子

1

粉丝