文章目录
- 加载域的结束地址并不是固件的结束地址?
- ROM中执行域的描述
- RAM中执行域的描述
- 问题分析
- 中断向量表在固件中的存储位置
- 代码段在固件中的位置
- 只读数据
- Regin$$Table
- RW Data段
- 其中的内部机理
- 总结
MAP 文件分析可以参考之前的文章
程序代码在未运行时在存储器空间内称为加载域,在运行时在存储器空间内称为运行域。
加载域的结束地址并不是固件的结束地址?
在这里我们根据加载域的描述可以知道加载域的空间结束位置应该为0x08020000+0x00010978=0x08030978的位置。从下面的固件我们可以知道固件的实际结束位置应该是0x080305E8。这里的俩个位置并不一致是什么原因呢?我们继续往下逐步分析。
ROM中执行域的描述
上图内容的最后一行:结束地址为0x080304e0+0x20=0x08030500,和第一行中的描述一致。
根据类型和属性可以知道这一部分的内容为代码段(Code)和只读数据段(RO Data)
起始的第一个段为只读数据段,这个段的内容为中断向量表的数据。
这里我们根据上图计算下:
代码段大小:0x0802f754-0x08020188= 0x000f5cc对应于十进制为62924
只读数据段大小:0x08030500-0x0802f754+0x188=0xf34对应于十进制为3892
计算结果刚好和MAP文件中固件信息汇总处的结果一致
RAM中执行域的描述
根据类型和属性信息我们可以知道这一部分内容为RW Data和ZI Data
这里我们根据上图计算:
RW Data大小:0x478对应于十进制为1144
ZI Data:0x49b0+0x2000-0x478=0x6538对应于十进制为25912
Grand Totals - 显示映像文件的真实大小
ELF Image Totals - 可执行链接格式映像文件大小(如果使用RW数据压缩来优化ROM大小,则最终镜像的大小会发生变化)
ROM Totals - 显示包含镜像所需的ROM的最小大小
上面的计算结果和真实映像文件的大小是对应的,由于对RW数据进行了压缩,导致实际的RW空间会比上面的计算值小。
知识点:linker默认会将重复的RW数据压缩。
上图中最后一行0x200049b0+0x2000=0x200069b0刚好和栈顶地址一致:
问题分析
至此我们基本知道了原因,由于链接时会将重复的RW数据进行压缩,所以实际的固件大小会小于加载数据的大小,差值刚好为被压缩掉的RW数据空间的大小
中断向量表在固件中的存储位置
在MAP文件中我们可以知道 ,向量表的起始地址为0x08020000结束地址为0x08020188
对应于固件中的信息:
首地址为栈顶地址,
第二数据为复位函数的入口地址
后续内容依次为中断向量表中各个中断函数的入口地址。
代码段在固件中的位置
中断向量表的内容结束后便是代码段的内容
从MAP文件中可以看出0x08020188位置开始到0x0802f754位置结束存储的内容便为代码段的程序代码
如下图红框中的位置开始后续便为程序代码的数据
…
上图为代码段结束部分的位置
只读数据
代码段内容结束后紧跟着就是只读数据段
从下面的map文件可知0x0802f754到0x0803500之间存储的便是只读数据
这里我们去上图长框位置的CRC32Table的数据进行查看,对应固件位置如下图所示:
上图红框位置开始后续的数据和下面表中的数据是完全一致的。
Regin$$Table
之前的文章中有分析这个数据表,往RAM空间加载数据依据的便是这个表提供的信息。
RW Data段
单纯从MAP文件看RW Data数据需要0x478字节的空间
在固件文件中从0x0803500一直到程序的结束存储的便是RW Data数据的内容,共占0xE8字节的空间,从上面的分析中可以知道,实际生成固件中的RW Data是压缩过后的数据,其中去掉了重复数据,等程序运行时在RAM中再将数据展开,这样有助于节省ROM空间。
其中的内部机理
对于没有赋初值或初始化为0的全局变量,会等到程序运行时再在RAM中划分出一块区域并初始化为0,这时你可能会纳闷程序代码中怎么知道变量在RAM中划分区域的位置呢?答案是程序实际就是知道,并且程序中每次需要访问变量的位置在程序代码中变量指向的空间就是RAM中对应的位置。你可能会感觉很神奇,但结合之前的Regin$$Table分析你可能就会感到豁然了,毕竟RAM中的空间分配早就在固件生成时都定义好了,固件中也已经记录了这些信息。
那对于RW Data而言其实道理也是一样的,只不过RW Data对应的数据是有初值的。这里我们只需将全部全局变量的值记录下来并去掉重复数据后存储到固件中,在程序运行时在RAM中创建了对应的全局变量空间后将初值赋值给对应的变量即可。不过这好像还有个疑问:运行时怎么知道将那个值赋值给哪个变量呢?后续再继续探究下。
总结
固件各段在Flash中的存放顺序,以及运行时加载到SRAM中的顺序如下: