STM32开发笔记(三)——通过map文件了解堆栈分配(STM32、MDK5)避免堆栈溢出

本文介绍了STM32开发中遇到的栈溢出问题,通过分析.map和Startup.s文件理解堆栈分配。作者强调了栈用于局部变量和函数调用,堆用于动态内存分配,并展示了堆栈在SRAM的分布。为了避免栈溢出,建议关注变量大小和堆栈设置,适当调整堆栈大小,并在MDK5中修改Startup.s文件。

STM32开发笔记(二)——通过map文件了解堆栈分配(STM32、MDK5)--避免堆栈溢出

小狼@http://blog.youkuaiyun.com/xiaolangyangyang


    在最近的一个项目的开发中,每当调用到一个函数,程序就直接跑飞。debug跟进去看不出什么逻辑错误,但发现函数内局部变量声明之后,全局变量的值被清零,后来查看局部变量地址已经超出栈的范围,于是确定是栈溢出。如果不稍微了解一下堆栈,在开发过程中可能碰到各种奇怪的错误。

.map和startup.s文件

MAP 文件是程序的全局符号、源文件和代码行号信息的唯一的文本表示方法,它可以在任何地方、任何时候使用,不需要有额外的程序进行支持。

在MDK5中,在项目中双击Target就能自动打开.map文件。

技术图片

Startup.s文件是系统的启动文件,主要包括堆和栈的初始化配置、中断向量表的配置以及将程序引导到main( )函数等。

Startup.s主要完成三个工作:栈和堆的初始化、定位中断向量表、调用Reset Handler。

堆栈作用

栈(stack)空间,用亍局部变量,函数调时现场保护和返回地址,函数的形参等。

堆(heap)空间,主要用亍劢态内存分配,也就是说用 malloc,calloc, realloc 等函数分配的变量空间是在堆上。

堆栈在内存分布

在map文件中搜索STACK或者HEAP,在接近文件底部的位置可以看到SRAM的分配,如下图。

技术图片

 从上图中可以看出SRAM空间用来存放:1、各个文件中声明和定义的全局变量、静态数据和常量;2、HEAP区;3、STACK区。

STM32的堆栈是存放在SRAM中的,分配堆栈大小需要考虑SRAM容量。

在.map文件中的Image Symbol Table底下可以找到如下图所示堆栈分布信息。

技术图片

堆在使用时会从低地址往上加,而栈是从__initial_sp开始往下减。以上图中的堆栈地址为例,malloc会从0x20002248开始往上加,局部变量的分配会从0x20004448开始往下减。如果入栈元素过大,使得入栈元素的地址访问到了0x20002448之后的内容,就发生了栈溢出,首先会改变堆中的元素值,如果入栈元素够大,可能会直接改变HEAP后面的全局变量。同理,当动态申请的内存过大时,堆中变量越界到栈中,此时就发送堆溢出。

避免产生这类错误的产生,程序设计时就应该考虑变量大小和堆栈大小是否合适。一个是减少过大的临时变量和动态申请内存,另一个是在SRAM空间允许的情况下增大堆栈大小,如上图中栈大小是8192字节,堆大小是512。

堆栈大小设置

MDK5中可以通过修改startup.s文件来设置堆栈大小,只需要修改startup.s文件中的Stack_Size和Heap_Size即可,如下图所示。

 技术图片

KEIL Uvison5中默认生成的startup.s文件是只读的,无法修改,只需要设置一下该文件的属性,把只读取消即可。


通过map文件了解堆栈分配(STM32、MDK5)--避免堆栈溢出

stm32 map文件的分析

嵌入式Map文件解析

<think>好的,我需要帮助用户了解如何通过GCC生成的MAP文件来分析STM32项目的堆栈使用情况。首先,我应该回忆一下关于MAP文件结构和堆栈分配的相关知识。根据用户提供的引用,特别是引用[1]提到通过MAP文件了解堆栈分配,而引用[2]讨论了启动文件的分析,但这里用户更关注GCC环境下的情况。 首先,我需要确认GCC生成的MAP文件MDK(如Keil)生成的有何不同。GCC的MAP文件通常包含内存区域的分配情况,包括各个段(如.text、.data、.bss)的地址和大小,以及符号表等信息。堆栈信息通常是在启动文件(startup_stm32xxxx.s)中定义的,但具体的位置和大小可能在链接脚本中指定。 接下来,用户需要检查堆栈使用情况,这通常涉及到查找堆栈区域的起始地址和大小。在链接脚本(如STM32的链接器脚本.ld文件)中,通常会有定义堆栈大小的部分,例如`_estack = ORIGIN(RAM) + LENGTH(RAM);`,这表示堆栈的结束地址是RAM的末尾。堆栈的大小则由链接脚本中的`_Min_Stack_Size`定义。 在MAP文件中,可以通过搜索这些符号来找到堆栈分配情况。例如,查找`_estack`的地址,以及堆栈大小的数值。此外,用户可能需要检查各个函数和变量的内存占用,以评估是否存在堆栈溢出的风险。特别是全局变量和静态变量会占用.data或.bss段,而局部变量和函数调用会影响堆栈的使用。 用户可能需要使用工具来分析堆栈的最大使用量,例如通过GCC的编译选项生成反汇编文件,或者使用静态分析工具。但MAP文件本身只能提供静态的信息,动态的堆栈使用情况可能需要运行时检查,比如填充堆栈空间并检测是否溢出。 此外,引用[1]提到避免堆栈溢出的方法,这可能包括在MAP文件中检查堆栈区域是否与其他内存区域有重叠,或者是否有足够的剩余空间。用户需要关注堆栈指针的初始值(通常是RAM的末尾)以及堆栈大小的定义是否足够应对最深的函数调用层次和局部变量的使用。 总结步骤应该是:生成MAP文件,查找堆栈相关的符号,分析链接脚本中的堆栈配置,评估内存使用情况,并使用工具进行进一步分析。可能还需要提及如何修改链接脚本调整堆栈大小,以及常见的堆栈溢出检测方法,比如使用调试器或硬件断点。 需要注意用户可能对GCC工具链不熟悉,因此需要详细说明每个步骤,例如如何生成MAP文件(在链接时添加-Wl,-Map=output.map选项),如何在MAP文件中搜索关键字,如何理解输出中的各个部分。同时,可能存在的误区,例如堆栈大小不足而未调整链接脚本,或者局部变量过大导致栈溢出,这些也需要提醒用户。 最后,根据用户要求,生成相关问题,可能涉及链接脚本修改、动态堆栈分析工具、堆栈溢出调试方法等。</think>### 通过GCC生成的MAP文件分析STM32堆栈使用情况 #### 1. **生成MAP文件** 在GCC编译时添加链接选项 `-Wl,-Map=output.map`,例如在Makefile中: ```makefile LDFLAGS += -Wl,-Map=$(BUILD_DIR)/project.map ``` 编译完成后,生成的`project.map`文件会包含内存分配详情[^1]。 #### 2. **定位堆栈定义** - **链接脚本中的堆栈配置** 堆栈大小通常在链接脚本(`.ld`文件)中定义,例如: ```ld _Min_Stack_Size = 0x800; /* 2KB */ ``` 堆栈结束地址(栈顶)一般指向RAM末尾: ```ld _estack = ORIGIN(RAM) + LENGTH(RAM); ``` - **在MAP文件中搜索符号** 打开MAP文件,搜索关键词`_estack`和`_Min_Stack_Size`,例如: ``` .stack 0x20005000 0x800 0x20005000 _estack = . + 0x800 ``` 这里显示栈顶地址为`0x20005000`,堆栈大小为`0x800`(2KB)。 #### 3. **分析内存占用** - **全局变量与静态变量** 在MAP文件的`Data`和`BSS`段中查找大内存对象: ``` .data 0x20000000 0x400 ... .bss 0x20000400 0x200 ``` 若这些段占用过多RAM,可能导致堆栈空间不足。 - **函数调用深度** MAP文件无法直接显示动态堆栈使用,需结合反汇编(通过`objdump -d`生成)估算最大嵌套调用。 #### 4. **堆栈溢出检测方法** - **静态分析工具** 使用`arm-none-eabi-size`查看内存占用: ```bash arm-none-eabi-size project.elf ``` 输出示例: ``` text data bss dec hex filename 10240 512 2048 12800 3200 project.elf ``` 若`data + bss + 堆栈`接近RAM总大小,需调整堆栈[^1]。 - **运行时检测** 在启动文件中初始化堆栈时填充特定模式(如`0xDEADBEEF`),运行时检查是否被覆盖。 #### 5. **修改堆栈大小** 在链接脚本中调整`_Min_Stack_Size`并重新编译: ```ld _Min_Stack_Size = 0x1000; /* 增大至4KB */ ``` #### 示例分析 假设MAP文件显示: ``` RAM 0x20000000 0x10000 ... .stack 0x2000f000 0x800 ``` 若RAM总大小为64KB(`0x10000`),堆栈从`0x2000f000`开始占用2KB,则剩余RAM为: ``` 0x10000 - (0xf000 - 0x20000000 + 0x800) = 0x2800 (10KB) ``` 此时需确保`data + bss`不超过10KB,否则可能溢出
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值