单片机启动文件——数据段重定位,BSS段清零
目录
- 重定位概念的引入
- 一、数据段重定位
- 1.作用:
- 2.目的:
- 3.自己模拟代码
- 二、BSS段清零
- 1.作用:
- 2.目的:
- 3.自己模拟代码
- 三,实现原理
重定位概念的引入
单片机中内存段的详细介绍
在单片机中内存分为了很多不同的区域,在上面的文章中可以看到,flash与RAM中有两段空间的信息在flash与RAM都保持了,分别是RWdata段与bss段,在启动文件中对这两块区域分别进行了数据段重定位与BSS段清零,而对应的代码是由编译器根据散列文件自动生成的,我们看不到,但是我们可以不让编译器自动生成自己进行数据段重定位与BSS段清零
数据段重定位与BSS段清零对应的代码是由编译器根据散列文件自动生成的,我们看不到,我们可以通过反汇编文件以及Debug时的反汇编窗口看到编译器给我们生成的代码如何让keil编译生成bin文件与反汇编文件?,同时我们可以不让编译器自动生成自己进行数据段重定位与BSS段清零
操作方法就是
1.不使用main的函数名,使用其它函数名
一、数据段重定位
1.作用:
-
将.data段从非易失性存储器(如Flash/ROM)复制到易失性存储器(RAM)中的目标位置。
-
链接器在编译链接阶段,会将已初始化的全局变量和静态变量放入.data段,并将它们的初始值存储在Flash中。
-
但是程序运行时,这些变量必须位于RAM中才能被修改。
-
启动文件负责将存储在Flash中的初始值数据“搬运”到RAM中对应变量的地址处。
2.目的:
-
确保初始化变量拥有正确的初始值:如果.data段没有从Flash复制到RAM,那么所有初始化为非零值的全局变量和静态变量在程序启动时将处于未定义状态(RAM上电后的值是随机的),而不是程序员设定的初始值。这会导致程序行为完全不可预测。
-
满足C/C++语言规范要求:C/C++标准规定全局变量和静态变量必须在程序启动时完成初始化(如果有初始值)。重定位是实现这一语言要求的底层机制。
-
实现变量的可写性:Flash通常是只读的(在运行时),而变量需要被程序读写。将初始值复制到RAM中是变量可写的先决条件。
3.自己模拟代码
/*启动文件模拟过程代码*/IMPORT |Image$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$Length|IMPORT |Load$$RW_IRAM1$$Base|LDR R0, = |Image$$RW_IRAM1$$Base| ; DESTLDR R1, = |Load$$RW_IRAM1$$Base| ; SORUCELDR R2, = |Image$$RW_IRAM1$$Length| ; LENGTH //根据ARM架构AAPCS法则模拟函数参数BL My_Copy
/*C语言实现拷贝逻辑*/
void My_Copy(char* const Destination,char* const Soruce,int Size)
{int i = 0;for(;i < Size;i ++){Destination[i] = Soruce[i];}return;
}
二、BSS段清零
1.作用:
-
将.bss段在RAM中对应的内存区域全部填充为0。
-
链接器将未初始化或显式初始化为0的全局变量和静态变量放入.bss段。.bss段在程序映像(存储在Flash中)中不占用实际存储空间,它只记录需要在RAM中预留多少空间以及起始地址。
-
启动代码需要找到.bss段在RAM中的起始地址和长度,然后将这块内存区域清零。
2.目的:
-
确保未初始化的变量从0开始:C/C++标准规定未显式初始化的全局变量和静态变量在程序启动时必须被初始化为0(对于基本类型是0,对于指针是NULL)。如果不清零.bss段,这些变量在启动时将包含RAM上电后的随机垃圾值,导致程序行为不可预测,尤其是指针变量可能指向非法地址。
-
节省Flash存储空间:由于.bss段中的变量初始值都是0,没有必要在Flash中存储一大串0值。启动时直接清零对应的RAM区域是更高效的方法(空间换时间)。
-
满足语言规范要求:与.data段重定位一样,BSS清零是实现C/C++语言对未初始化/零初始化全局变量和静态变量初始化要求的底层机制。
3.自己模拟代码
/*启动文件模拟过程代码*/IMPORT |Image$$RW_IRAM1$$ZI$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Length| LDR R0, = |Image$$RW_IRAM1$$ZI$$Base| ; DESTLDR R1, = 0 ; SORUCE LDR R2, = |Image$$RW_IRAM1$$ZI$$Length| ; LENGTH BL My_Zero_Init //根据ARM架构AAPCS法则模拟函数参数BL My_Zero_Init
/*C语言实现清零逻辑*/
void My_Zero_Init(char* const Destination,int Soruce,int Size)
{int i = 0;for(;i < Size;i ++){Destination[i] = Soruce;}return;
}
三,实现原理
C语言-》编译器-》机器码(汇编就是机器码的注释)
展开能力,C语言与汇编的后沟就是编译器,C语言是编程工具,汇编语言是结果
判断结果是否符合预期?学会看反汇编
如何控制结果符合预期?学会一定的C语言语法以及编译器的工作特性
针对keil平台,控制编译器的方法就是分散加载文件
我们可以通过分散加载文件的语法来控制代码函数变量的链接地址
理论上也可以控制加载地址针对使用烧录器来说因为烧录器使用的是hex文件包含程序烧录的地址信息,但是一般来说程序编译后使用bin二进制文件这个文件使用特定的烧录工具,烧录的是纯机器码即代码指令不包含代码加载地址信息。
分散加载的语法可以参考分散加载文件与attribute关键字使用说明
同时分散加载文件中也包含一些符号信息,我们在代码中可以使用特定的语法来引入这些符号的值,RW-data段的加载地址大小,搬运的目的地址,同理bss段数据也是同理。
导入符号的方法有
/*汇编文件使用*/IMPORT |Image$$ER_IROM1$$Base|IMPORT |Image$$ER_IROM1$$Length|IMPORT |Load$$ER_IROM1$$Base|IMPORT |Image$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$Length|IMPORT |Load$$RW_IRAM1$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Base|IMPORT |Image$$RW_IRAM1$$ZI$$Length|
/*C语言文件使用*/extern int Image$$ER_IROM1$$Base;extern int Image$$ER_IROM1$$Length;extern int Load$$ER_IROM1$$Base;extern int Image$$RW_IRAM1$$Base;extern int Image$$RW_IRAM1$$Length;extern int Load$$RW_IRAM1$$Base;extern int Image$$RW_IRAM1$$ZI$$Base;extern int Image$$RW_IRAM1$$ZI$$Length; extern int Image$$ER_IROM1$$Base[];extern int Image$$ER_IROM1$$Length[];extern int Load$$ER_IROM1$$Base[];extern int Image$$RW_IRAM1$$Base[];extern int Image$$RW_IRAM1$$Length[];extern int Load$$RW_IRAM1$$Base[];extern int Image$$RW_IRAM1$$ZI$$Base[];extern int Image$$RW_IRAM1$$ZI$$Length[]; extern int * Image$$ER_IROM1$$Base;extern int * Image$$ER_IROM1$$Length;extern int * Load$$ER_IROM1$$Base;extern int * Image$$RW_IRAM1$$Base;extern int * Image$$RW_IRAM1$$Length;extern int * Load$$RW_IRAM1$$Base;extern int * Image$$RW_IRAM1$$ZI$$Base;extern int * Image$$RW_IRAM1$$ZI$$Length;