STM32之启动文件(startup...)中堆栈初始化分析

公众号:嵌入式不难

本文仅供参考学习,如有错误之处,欢迎留言指正。

一、官方原始代码及内存分配

1. startup_stm32f103xb.s文件中,内存分配的原始汇编代码

```
; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
;EQU汇编指令用于为程序中的常量、标号等定义一个等效的字符名称,类似c中#define
;	此指令编译器并不会为次字符分配内存,仅仅是为了便于阅读
;AREA 段名 属性1 ,属性2 ,……   用于定义一个代码段或数据段
;	STACK为段名
;	NOINIT指定此数据段仅仅保留了内存单元(初始为0)
;	READWRITE指定本段可读可写
;	ALIGN=3指定该段地址按2^3字节字节对齐
;SPACE用于分配一片连续的存储单元(字节为单位)。
;	SPACE指令前的LABEL即此例中的Stack_Mem表示此段的起始地址
;__initial_sp 以下基于我自己的理解,事实证明我的理解也是很正确的
;	首先标号可能一个字符,也可能是一个地址,标号必须顶格写
;	它用于表示一个字符时,即是EQU前的标号用法
;	它用于表示一个地址时,类似于c中的指针,即是SPACE前的标号的用法
;	现在Stack_Mem起始地址已经分配了Stack_Size大小空间了,然后这一段空间已经被使用了,
;	所以下一个标号地址一定是紧接着上一个被使用的地址后的地址,即是__initial_sp=Stack_Mem+Stack_Size
Stack_Size		EQU     0x400	

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size      EQU     0x200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit
```

2.编译生成的.map文件截取

1).截取部分.map文件内容
HEAP                          0x20000030   Section      512  startup_stm32f103xb.o(HEAP)
STACK                         0x20000230   Section     1024  startup_stm32f103xb.o(STACK)

__heap_base                   0x20000030   Data           0  startup_stm32f103xb.o(HEAP)
__heap_limit                  0x20000230   Data           0  startup_stm32f103xb.o(HEAP)
__initial_sp                  0x20000630   Data           0  startup_stm32f103xb.o(STACK)

2).解析
由上述startup_stm32f103xb.s汇编文件可知
HEAP			  -->  表示堆的数据段名,指向了堆的起始地址
STACK			  -->  表示栈的数据段名,指向了栈的起始地址
__heap_base		  -->  指向了堆的起始地址
__heap_limit	  -->  指向了堆的结束地址
__initial_sp	  -->  指向了栈的起始地址
注意:
①.这里指的起始地址和结束地址均表示地址由小到大排列,即是小地址为起始地址,大地址为结束地址
②.堆增长方向:向上生长
③.栈增长方向:向下生长

二、疑点

1. 基础疑点

  • 1.1. 为什么.map文件中并没有HEAP/STACK/__heap_base/__heap_limit/__initial_sp定义
  • 1.2. 为什么.map文件中只有STACK和__initial_sp,没有HEAP/__heap_base/__heap_limit的定义

2. 增强疑点

  • 1.1. 为什么.s文件中 AREA STACK, NOINIT, READWRITE, ALIGN=3 和Stack_Mem SPACE Stack_Size的代码行数明明在AREA HEAP, NOINIT, READWRITE, ALIGN=3和Heap_Mem SPACE Heap_Size的代码行数前定义,结果.map文件中__initial_sp(栈结束地址)还要比__heap_limit(堆结束地址)大呢?难道不是定义在前面的区域就先分配吗?

三、解疑方法(实验方法)

1. 基础疑点解答

  • 首先确定Options中Listing栏Linker Listing 下方所有的选项保持选中
    • 1.1. 没有勾选Options中Target栏USE MicroLIB项
    • 1.2. 因为程序中并没有使用堆空间,编译器自动优化不适用堆空间,需用函数malloc使用堆空间

2. 增强疑点解答

  • 实验方案1:可先调换申请堆空间和栈空间的申请代码,观察.map文件是否有变化
    • 实验方案1步骤1:调换申请堆空间和栈空间的申请代码,结果如下

      ; <h> Heap Configuration
      ;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
      ; </h>
      
      Heap_Size      EQU     0x200
      
                      AREA    HEAP, NOINIT, READWRITE, ALIGN=3
      __heap_base
      Heap_Mem        SPACE   Heap_Size
      __heap_limit
      
      ; Amount of memory (in bytes) allocated for Stack
      ; Tailor this value to your application needs
      ; <h> Stack Configuration
      ;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
      ; </h>
      
      Stack_Size		EQU     0x400
      
                      AREA    STACK, NOINIT, READWRITE, ALIGN=3
      Stack_Mem       SPACE   Stack_Size
      __initial_sp
      
    • 实验方案1步骤2:编译后观察.map文件地址,结果如下

      __heap_base                 0x20000030   Data           0  startup_stm32f103xb.o(HEAP)
      __heap_limit                0x20000230   Data           0  startup_stm32f103xb.o(HEAP)
      __initial_sp                0x20000630   Data           0  startup_stm32f103xb.o(STACK)
      
    • 实验方案1分析:__heap_base、__heap_limit、__initial_sp与调换前的地址一致,说明调换代码前后顺序并不能改变编译结果,即调换代码位置并不能改变堆栈的地址

  • 实验方案2:在实验方案1的结论下,先将.s文件的顺序还原至初始状态,在此基础上,再添加一段申请数据段内存的代码,观察申请的内存处于什么位置

    • 实验方案2步骤1:添加一段申请内存的代码。结果如下
      • 1)…修改.s文件片段1–>申请一个数据段
        MY_Test_Size    EQU     0x100
        
                        AREA    MY_Test_Base, NOINIT, READWRITE, ALIGN=3
        __MY_Test_Start
        MY_Test_Data    SPACE   MY_Test_Size
        __MY_Test_End
        
        ; Amount of memory (in bytes) allocated for Stack
        ; Tailor this value to your application needs
        ; <h> Stack Configuration
        ;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
        ; </h>
        
        Stack_Size		EQU     0x400
        
                        AREA    STACK, NOINIT, READWRITE, ALIGN=3
        Stack_Mem       SPACE   Stack_Size
        __initial_sp
        
        ; <h> Heap Configuration
        ;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
        ; </h>
        
        Heap_Size      	EQU     0x200
        
                        AREA    HEAP, NOINIT, READWRITE, ALIGN=3
        __heap_base
        Heap_Mem        SPACE   Heap_Size
        __heap_limit
        
      • 2)…修改.s文件片段2–>外部声明变量
        IF      :DEF:__MICROLIB           
        
        EXPORT  __initial_sp
        EXPORT  __heap_base
        EXPORT  __heap_limit    
        EXPORT  __MY_Test_Start
        EXPORT  __MY_Test_End
        
        ELSE
        
      • 3)…修改main.c文件片段2–>外部引用变量,防止编译器优化
        extern int  *__MY_Test_Start;
        extern int  *__MY_Test_End;
        temp_all = *__MY_Test_Start;
        temp_all = *__MY_Test_End;
        
    • 实验方案2步骤2:编译后观察.map文件地址,结果如下
      HEAP                    0x20000030   Section      512  startup_stm32f103xb.o(HEAP)
      MY_Test_Base     		0x20000230   Section      256  startup_stm32f103xb.o(MY_Test_Base)
      STACK                   0x20000330   Section     1024  startup_stm32f103xb.o(STACK)
      
      __heap_base             0x20000030   Data           0  startup_stm32f103xb.o(HEAP)
      __MY_Test_Start         0x20000230   Data           0  startup_stm32f103xb.o(MY_Test_Base)
      __heap_limit            0x20000230   Data           0  startup_stm32f103xb.o(HEAP)
      __MY_Test_End           0x20000330   Data           0  startup_stm32f103xb.o(MY_Test_Base)
      __initial_sp            0x20000730   Data           0  startup_stm32f103xb.o(STACK)
      
    • 实验方案2分析:由实验结果可知新申请的内存插在了堆栈之间,占据了0x20000230至0x20000330内存空间,就显得更加奇怪了,怎么会跑到这儿去了呢,通过我两天的测试,终于找到答案,首先需要分配一段内存肯定得通过AREA伪指令,然后AREA伪指令后面的段名和属性成了两个关键点,MDK-ARM编译器会根据属性来判断存储于什么位置,再根据段名首字母来判断先为谁分配内存,顺序表现为A开头段名最优先,Z开头段名排最后,由上述可知HEAP段应最先分配,MY_Test_Base段第二分配,STACK段最后分配,通过对比试验数据可得知此结论成立

四、结论

  • AREA伪指令后面的段名和属性是两个关键点,MDK-ARM编译器会根据属性来判断存储于什么位置,再根据段名首字母来判断先为谁分配内存,顺序表现为A开头段名最优先,Z开头段名排最后
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值