TI的编译器生成的目标文件是一种模块化的ELF格式文件,代码和数据在ELF文件中以段的形式组织,一个ELF段是在内存空间中占连续一块code或data。ELF文件中包含了段的完备信息,如段的绝对地址.名字.属性以及数据等。
这些段可分为两种,已初始化段和未初始化段。已初始化段主要包括数据表和可执行代码,如:.text,.cinit,.const等;未初始化段用于保留存储器空间,程序利用这些空间在运行时创建和存储变量,如.bss段,.stack段,.far段,有些段,如.sysmem段,如果C程序没有用到malloc,calloc等这些函数来分配存储器空间的话,编译器就不创建.sysmem块。
每个段的具体内容是:
.text 存放可执行代码和浮点数常量
.cinit C语言全局变量和静态变量的C初始化记录列表,代码运行时要对应拷贝到相应的变量空间中
.const 存放已明确初始化的字符串常量,全局常量和静态常量
.bss 存放没有初始化的全局变量和静态变量。在小模式中,.bss块也为常数宏保留空间。在程序开始运行时,数据从.cinit块中拷贝至
.stack 为系统堆栈分配存储器。这个存储器用于将变量传递至函数以及分配局部变量。
.far 以far声明的全局/静态变量
.sysmem 为动态存储器函数malloc、calloc、realloc分配存储器空间。
注意DSP与ARM编译器不一样的地方在于DSP的C语言全局初始化变量和全局静态变量与全局未初始化变量和函数内部静态变量都是放在.bss中的,DSP的C语言全局初始化变量在运行之前是并不占用空间的,换句话说在生成的目标文件里,DSP全局初始化变量的值是在.cinit段对应记录中,DSP汇编语言的初始化变量是放在.data段。而ARM的全局初始化变量是存放在.data段,未初始化变量存放在.bss段,并不将C和汇编的段分开。
对于.bss中的全局初始化变量的初始化过程是可以通过编译选项来选择不同的初始化过程,它们是选项-c和-cr 。-c为运行时初始化,-cr为加载时初始化。在编译器生成目标文件中,会将C程序中初始化的全局/静态变量的初始值按一定结构放在.cinit中,但实际全局/静态变量占用的地址空间在.bss段中。如果是-c选项,那么C初始化函数c_int00()会读取.cinit段中的记录信息,分别初始化.bss段中的全局/静态变量。如果是-cr选项,那么全局/静态变量的初始化工作由loader程序完成,而不是c_int00()函数。也就是加载程序后,loader读取.cinit段的内容,然后初始化.bss的全局/静态变量,当使用JTAG调试时,CCS开发环境就是一个loader。
每个段都有一个load和run属性,load属性告诉loader或flash烧录程序在哪里放置这个段,run属性指出设备复位后这个段从哪里执行。所以,如果load属性和run属性不同,这个段将由二级bootloader从load地址拷贝到run地址,所有对段的references都是指它的run地址。一个段的run地址基于CPU访问该段的频率,如果一个段只被CPU访问一次,这个段就可以没有run地址,这样可以节省RAM空间,比如.cinit段一般只在boot时访问一次。而需要CPU快速访问的段则在RAM中有一个run地址。工程编译后的.map文件中包含了段的link信息,如段的size,load地址和run地址,我们可以从里边找到相关信息。