周末不卷了,讲个知识点很散的LD文件,这东西难度完全看原来的LD文件怎么写,我们顶多就是改改,要是从零开始写的话,人都要到异世界生活了。
说在前面
它不像代码一样都是要定义的,大部分都是标号,没定义过的标号第一次出现就是给他定义。
它的注释一般是C语言那种/**/,其他的不知道能不能用
它有几个大块,花括号{}外面部分、MEMORY部分、SECTIONS部分、其他带花括号的部分。
花括号{}外面部分
这些比较简单,就是一些宏定义。
ENTRY入口地址
ENTRY(Reset_Handler)
以Reset_Handler作为进程执行的第一条用户空间的指令在进程地址空间的地址,简单来说就是代码从这里开始执行,Reset_Handler一般在startup之类的汇编文件里面写着。
DEFINED检查定义
HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x00000400;
DEFINED(__heap_size__) 检查__heap_size__是否已经被定义,如果有定义就返回1,没定义就返回0。但是这里后面跟着? __heap_size__ : 0x00000400;意义就变了,变成了像C语言一样“DEFINED(__heap_size__) ? __heap_size__ : 0x00000400”成了一个整体。
如果__heap_size__有定义就返回__heap_size__的值,没定义就返回0x00000400。
MEMORY部分
就是MEMORY然后用花括号括住的部分,像刻光盘一样,给你的芯片分个区,这里仅仅只是分区,不会塞东西进去。这里具体要分多少个区域,得根据芯片来,而且每个芯片的ROM和RAM区域都不一样。
MEMORY
{
iROM_0 : ORIGIN = 0x00000000, LENGTH = 1206k
iRAM_0 : ORIGIN = 0x04000000, LENGTH = 32k
}
有像上面这样写的最简单的,分个iROM_0区域,0x00000000开始,长度为1206k。
MEMORY
{
/* Flash */
m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000400
m_text (RX) : ORIGIN = 0x00000410, LENGTH = 0x000EBBF0
/* SRAM */
m_data_2 (RW) : ORIGIN = 0x1FFF4800, LENGTH = 0x0001A000
MagicFlag (RW) : ORIGIN = 0x2000EB00, LENGTH = 0x00000500
}
有像这样写得详细一点的,分开flash和RAM区域,还标注清楚权限。
SECTIONS部分
SECTIONS是最重要的,中文翻译叫段。不仅细化了MEMORY的分区,还能嵌套别的{},SECTIONS里面的{}也叫SECTIONS,中文翻译叫做节。
无嵌套例子
先举个没有嵌套的例子
SECTIONS
{
/* Start of internal ROM area (iROM_0) */
.intvect align (512) :>iROM_0 /* start of interrupt vector */
.intvect_end 0x00200 :>. /* end of interrupt vector */
EIINTTBL align (4) :>.
.text align(4) :>. /* program code area */
.romdata ROM(.data) :>. /* constant data to initialize variables (copied to RAM at startup)*/
.romzdata ROM(.zdata) :>. /* constant data to initialize variables in ZDA area (copied to RAM at startup)*/
__top_of_iROM_0 = MEMENDADDR(iROM_0);
}
这里只提取了iROM_0的部分,我们逐句解析。
.intvect align (512) :>iROM_0
.intvect_end 0x00200 :>.
EIINTTBL align (4) :>.
.text align(4) :>. /* program code area */
先分了个叫".intvect"的中断向量表的区域出来,这里的SECTIONS名前面都要加点号。align是个对齐的命令,要开512字节的空间并且将里面中断向量表的内容对齐。:>就是放入的意思。把这512字节放到iROM_0里面,也就是上面的MEMORY里面的首地址0x00000000。
.就是当前地址的意思,由于上面已经占用了512字节,也就是0x200,那么下面的.会自动向后移动512字节。那就是把intvect_end放到0x200的位置。
后面的就不赘述了
.romdata ROM(.data) :>. /* constant data to initialize variables (copied to RAM at startup)*/
.romzdata ROM(.zdata) :>. /* constant data to initialize variables in ZDA area (copied to RAM at startup)*/
.romdata和.romzdata都是标号SECTIONS名,将iROM_0里面的.data区域分配给.romdata,.zdata区域分配给.romzdata,并且表明是ROM性质的。
__top_of_iROM_0 = MEMENDADDR(iROM_0);
将iROM_0最后的地址给到__top_of_iROM_0标号
MEMADDR(iROM_0)就是iROM_0开头地址
带嵌套例子
另一种格式也比较基础,也就是分了.sram和.heap 两个节。
.sram :
{
. = ALIGN(4);/*4字节对齐*/
*(.text.startup) /*取出.text.startup的所有的内容放在.sram里面*/
KEEP(*(.init))/*KEEP确保.init中的内容在链接过程中不会被垃圾处理法则丢弃。*/
__sram_bss_end = .;
} > int_sram /*放入上面MEMORY定义的int_sram分区当中*/
.heap (NOLOAD):
{
. += ALIGN(4);
_end = .;
end = .;
_heap_start = .;
. += HEAP_SIZE;/*将定位器.后移HEAP_SIZE长度*/
_heap_end = .;
} > int_sram
节地址
{
……
} > int_flash
. = ALIGN(4);
__text_end = .;
__sram_data_rom = __text_end;
.sram_data : AT(__sram_data_rom)
{
……
}
AT(__sram_data_rom)代表.sram_data节从__sram_data_rom的地址开始。
.DummySection : ALIGN(4)
{
……
} > Dummy
代表DummySection在前面地址对齐之后的地址开始
虚拟地址和加载地址
节里面的地址分为两种
VMA(virtual memory address虚拟内存地址)
LMA(load memory address加载内存地址)
VMA是执行输出文件时section所在的地址,而LMA是加载输出文件时section所在的地址。
通常VMA和LMA是相同的,也有不同的情况。
.sram_bss:
{
……
} > int_sram AT>int_sram
前面一个int_sram是VMA,后一个int_sram是LMA。
.sram_bss (NOLOAD) :
{
……
} > int_sram
这种就是int_sram是VMA,没有LMA。
其他带花括号的部分
这种就比较简单了,而且少见,就是最常用CONSTANTS。
CONSTANTS
{
const_data_section 0x08000000 :
{
*(.const_data) // 将程序中的只读数据放置到Flash存储器的起始地址0x08000000处
} > FLASH
}