class="markdown_views prism-atom-one-light">
最近各种忙着码代码,遇到了各种各样的坑,对CodeWarrior工程中的这个map文件的理解也越来越深了。闲来无事,和大家分享下对这个map文件的理解。可能还有不对之处,敬请指出。
前言
map文件保存了你的整个程序编译链接后的各种信息,包括编译器链接器信息,内存分配信息,对象依赖等,每次编译链接程序后,这个文件都会被覆盖重新生成。
对我来说,它最主要的作用是它详尽的描述了整个程序最终在内存中的分布情况,有助于我们工程师完全掌控每一个对象(函数/变量/常量/栈……),以及对象间的相互关系,加深对编译链接过程的理解。
其内容为文本形式,可以使用任意文本编辑器打开查看。
文件解释
就随便拿个最近在写的程序作为例子。默认的map文件名如上为Project.map。
打开后最上面 PROGRAM后跟着是项目abs文件的地址信息。
TARGET SECTION(编译目标)
里头列出了使用的处理器,链接器文件格式(ELF),使用的链接器的版本(CodeWarrior默认使用的是SmartLinker),地址模型。
地址模型主要分为:SMALL、BANKED、LARGE。差别主要是其默认的地址分配和寻址方式不同,想要详细了解的话可以参考
https://blog.csdn.net/lin_strong/article/details/78127072 里的相关介绍。
FILE SECTION(文件清单)
里头列举了用到的.o 文件,因为map是链接器生成的说明文件,对链接器来说它直接使用的是已经编译过的ELF格式的.o文件而不是最初的源代码.c文件。
三列分别是:文件名, 此文件用到的地址模型, 使用的语言(如:C语言:ANSI-C;汇编:Assembler)。
STARTUP SECTION
描述了程序启动相关的函数和变量:
起始地址(Entry point)是程序的启动入口,这里是_Startup函数,在Start12.c中,是工程自动创建的,在其中会完成程序启动前的一系列初始化工作,然后才会跳转到我们熟知的C语言入口main函数。
而_startupData这个初始化用的结构体则保存了初始化时用到的一些参数:
nofZeroOut描述了后面数组的大小,如这里可以看出pZeroOut指向的三个区域;
pZeroOut指向的区域会在初始化时被全部初始化为0,对应于C语言中的全局变量,观察下一个区块就可以看出他们之间的联系。
toCopyDownBeg则保留着要初始化的RAM变量的初始化值,它们保存在非易失内存中。在_Startup中会把初始值拷贝到对应的变量处完成初始化。
- 注:这里蛮提一个坑。可以看到第二个要清零的地址是24bit的,因为它们是放在分页区的向量,而在banked地址模型中默认的这个地址指针是16位的,所以要是直接编译的话就会看到0xF01000这个值变成了0x1000,那就变成从0x1000这个地址开始清零3204个字节了;因此如果使用了分页区的RAM的话要记得在编译器中添加-D__FAR_DATA选项,这样指针就会变成24bit的了。
SECTION-ALLOCATION SECTION(区块分配)
描述了每个数据段的名字、大小、类型(R只读、N/I不初始化、R/W可读写)、在存储器中的起始和结束地址、以及对应的分区(segment)。
我们可以看到.data、.bss和.common被连续分配到了RAM这个名字的分区中,对应这pZeroOut的第一个指针,在这几个段内的变量对应C语言中的全局变量。而PAGED_RAM被分配到了RAM_F0中,RAM_F1_744对应RAM_F1,分别对应pZeroOut的另两个指针,它们中是被分配到分页RAM中的全局变量。
.stack则是栈的位置,上例中从0x2006到0x2105,大小为256字节。
.abs_section_XXXX是编译器自动生成的段名,对应着程序中使用@直接定位地址的变量。
如:.abs_section_8对应的是MC9S12XEP100.h中的这一行
extern volatile PORTESTR _PORTE @(REG_BASE + 0x00000008UL);
这个块的最后我们可以看到一个summary,里头总结了各种类型的数据段实际占用的内存大小。
注:
代码量的计算并不是根据你文件编译出来的大小来确定的,链接器并不会链接那些没有用到的对象(除非用了某些语法强制进行链接),这些没用到的对象是不计入代码量的。所以程序实际上的大小并没有你在第一个图中看到的那个数字那么大。CodeWarrior 的试用版对代码量有限制指的就是这个最终的代码量。换句话说,不管你加再多代码,再多库到项目中;只要你不去用它们,一点代码量都不会增加。
VECTOR-ALLOCATION SECTION(中断向量分配)
描述了复位向量地址、初始值、以及具体的ISR函数名。
由于我这个工程内直接使用的向量表,而不是在prm文件中使用VECTOR 0 _Startup这样来定义ISR的,所以这边没有列出。
OBJECT-ALLOCATION SECTION (对象分配)
这个部分直接按照模块(C语言中就是不同的.c文件)把程序中的具体对象的详细信息列出。
包括对象的名字、所处模块、所在地址、十六进制表示的大小(hSize)、十进制表示的大小(dSize)、被引用次数、所在的段名。如果使用了reloacate把Flash上的程序重分配到了RAM中,还会跟上对应在新地址中的地址。
我们可以看到第一个对象就是刚刚举的那个地址在0x8处的变量的例子。其实际名字叫_PORTE,大小为1个字节,程序中没有任何地方直接使用到这个变量,但是因为他使用的是绝对定位,并没有被编译器优化掉。
同样的,地址如果是24bit的则说明使用了分页地址,16bit的说明使用的本地地址。
MODULE STATISTIC(模块统计)
列出不同模块的统计信息,包括 变量、代码、常量 的个数。
SECTION USE IN OBJECT-ALLOCATION SECTION
列出每个区块中分配的对象。
OBJECT LIST SORTED BY ADDRESS(地址顺序的对象清单)
类似
OBJECT-ALLOCATION SECTION,但是是按地址升序排列的,方便按地址查找。
UNUSED-OBJECTS SECTION(未使用对象)
列出不同模块中未使用的对象。这些对象不会占用实际内存。
COPYDOWN SECTION
变量初始化相关信息。
OBJECT-DEPENDENCIES SECTION (对象依赖表)
列出每个对象依赖于(或说引用了)哪些对象。
DEPENDENCY TREE(依赖树状图)
用树状图的方式形象的表示出来对象间的相互依赖关系。
注:
一个对象每被另一个对象依赖,它在OBJECT-ALLOCATION SECTION中的Ref计数就会加1。然后你比如就可以到这个树状体中查找到底它是被哪个对象使用了。
STATISTIC SECTION
一些统计信息
结语
以上就是对整个map文件的介绍。熟悉map文件,可以使我们更好的掌控自己的程序,快速定位各种问题,良好地分配有限的MCU内存资源。希望这篇文章可以对各位朋友有一定的帮助。