以Code Warrior 11生成的flash版本(FLASH.lcf)为例
一. 参考资料
安装完Code Warrior IDE之后,有一个自带的pdf帮助文件,叫做MCU_Power-Architecture_Compiler.pdf,即CodeWarrior Development Studio for Power Architecture Processors Build Tools Reference Manual。看下目录:
可谓面面俱到,整个编译过程中用到的方方面面都讲到了。通读一遍肯定大有裨益。
二. 链接配置文件
多个源代码文件编译成目标文件之后,会链接成为可执行文件。链接的过程,需要配置文件进行约束,比如哪些代码放在什么位置。嵌入式中比较明显,比如:
1. 0x0000_0000处的第一条指令,需要是复位异常处理指令;
2. 最开始的一段是中断向量表;
3. 初始化的代码需要放在最开始的4K区间,因为上电后CPU的访问空间限制在这4K里面,超出则无法访问;需要在这4K代码里面扩大CPU的访存区间;
4. 代码段、只读数据段放在flash地址区间,而可读写数据区和BSS区放在内存地址区间;
等等。
三. 实例
以CW11自动生成的FLASH.lcf文件为例。
1.MEMORY区间划分
总体划分整个地址区间,每一个区间取一个名字,然后起始地址和长度。
2.SECTIONS
接下来描述各个源文件中的代码,放在哪一个区间。
可以看到BAM即boot sector放在起始地址0处。然后是.init代码段和.init_vle代码段,存放初始化代码。其后是中断向量表。
代码段.text和.text_vle, .rodata段放在flash区间。注意的是.ctors和.dtors是C++的constructors和destructors即类的构造器和解构器。
带初始值可读写数据区.data(带初值的全局变量和静态变量)和无初值可读写数据区.bss (不带初值的全局变量和静态变量)处在内存地址区间。
这里“处在”的意思是:
a. 他们的地址是在这个区间,以这个区间的地址被引用;
b. 他们在可执行文件或者生成的image中的偏移量跟这个地址不对应;不可能生成一个image文件,从internal_flash结尾到0x4000_0000中间填充0,然后开始填充.data区;
问题来了,.data和.bss区存在两个地址:存在flash中的地址和存在内存中的地址。我们需要记录这两个起始地址和区间长度,在上电初始化时:
a. 从flash中拷贝.data区到内存中;
b. 把内存中.bss区清零;注意flash中不需要存放全部.bss区,因为没有带初始值,所以只需要记录起始地址和长度即可;这是其与.data不同的地方;
PS. 为什么有.data/.sdata/.sdata2和.bss/.sbss/.sbss2,这么多data区和bss区,这是ABI决定的。
3.堆和栈
堆和栈的区间在MEMORY中已经定义了。这里定义代码需要的变量,链接器会生成这些值并填充到代码中。
在初始化时,根据ABI,填充_stack_addr到Stack Pointer Register(r1/gpr1)。因为栈向下生长,所以_stack_addr加上了SIZEOF(stack),为高地址。
_heap_addr没有用拿到?ABI里面没有规定?调用malloc分配内存时即是从堆中分配,这个地址在这里用到,在库中引用:
三. CW生成image的注意事项
1. Linker生成了很多符号,参考__ppc_eabi_linker.h:
2. Linker需要-romaddr参数才会正确生成.data在flash中的地址: