OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*
指定输出的格式是32bits ARM 小端
OUTPUT_FORMAT原型为 OUTPUT_FORMAT(default, big, little),
在链接的时候,如果使用了-EB的命令行参数,则使用这里的big参数指定的字节序,
如果使用了-EL的命令行参数,则使用这里的little参数指定的字节序,
如果没有使用任何命令行参数,则使用这里的default参数指定的字节序。
由uboot.lds中的定义可见,不管在链接的时候使用了何种命令行参数,
输出的目标文件都是使用elf32-littlearm方式的字节序。
*/
OUTPUT_ARCH(arm)
//指出输出可执行文件的平台是arm
ENTRY(_start)
//指定输出可执行文件的起始入口码段是_start,_start在u-boot-2016.03-r0archarmcpuarmv7start.S
//代码分段的集合,{}内包含所有程序分段定义
SECTIONS
{
. = 0x00000000;
/*链接起始地址,ld最终链接时候,会被-Ttext $(TEXT_BASE)更新
生成的uboot.bin链接地址都是从TEXT_BASE开始
uboot分两个阶段(flash/ram)
flash阶段,程序实际从0地址开始执行,但程序的链接地址是从TEXT_BASE开始,
第一个阶段代码执行都是相对寻址,所以即使链接地址不对应,执行地址也不会出错
LDFLAGS += -Ttext $(TEXT_BASE) 定义在顶层目录的config.mk
这里的点”.”,是定位器符号(GNU风格的一个典型),把定位器符号置为0x00000000 (若不指定, 则该符号的初始值为0)。
指定系统启动从偏移地址零处开始。注意这只是个代码地址偏移值,真正的起始地址是由编译时指定的LDFLAGS指定的。
实际中改动这个值对输出没有影响*/
. = ALIGN(4);
//修改当前地址对齐方式为4字节对齐
.text :
{
*(.__image_copy_start)
*(.vectors)
CPUDIR/start.o (.text*)
*(.text*)
}
/*
.text:代码段,在此段中依次存放section(.__image_copy_start)、section(.vectors)、
section(start.o中所有的代码段 (.text*))、section(*(.text*))剩余的所有代码段。
*(.__image_copy_start):这里比较有意思的是section(.__image_copy_start),
在u-boot-2016.03-r0archarmlibsections.c中有如下定义:
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
这是一个0长度的数组,GNU 对C的扩展,实际达到的目的就是section(.__image_copy_start)
在链接时不占用链接地址空间,其后的section(.vectors)起始地址依然是0x00000000,
然后在__image_copy_start存放了一个地址,也就是0x00000000,相当于创建一个位置标签;
代表程序重定位(relocate)时拷贝的起始地址,
*(.vectors):字面上的意思就是向量段,
在u-boot-2016.03-r0archarmlibvectors.S中定义了.verctos段:.section ".vectors", "ax";
这是一个自定义的段标签的伪操作,含义是:定义一个段标签名为.vectors,且为允许、可执行段
CPUDIR/start.o (.text*):CPUDIR在根目录的Makefile中定义,
根据芯片;选择不同的start.S编译,编译出的start.o中.text段,链接到此处
*(.text*):剩余所有的代码都放到此段
*/
. = ALIGN(4);
/*代码段结束后,有可能不是4bytes对齐了,因此需要重新4bytes对齐,以开始后面的.rodata段*/
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
//重新4bytes对齐
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
/*
.u_boot_list:所有以u_boot_list名称开始的段,都链接于此,主要是一些uboot 命令相关代码;
KEEP关键字是为了保证所有的段都被加进来,SORT表示按照u_boot_list*后的段名子进行排序
*/
. = ALIGN(4);
.image_copy_end :
{
*(.__image_copy_end)
}
/*
__image_copy_ end存放了一个地址,取值待链接时确定,相当于创建一个位置标签;
代表程序重定位(relocate)时拷贝的结束地址
*/
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
/*
此处含义与__image_copy_start相同,定义一个位置标签,
表示重定位时全局变量的LABLE的起始地址;
关于重定位代码的说明需要另外说明
*/
.rel.dyn : {
*(.rel*)
}
//.rel.dyn:存放重定位代码时全局变量的位置信息
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
/*
__rel_dyn_end存放了一个地址,取值待链接时确定,相当于创建一个位置标签;
表示重定位时全局变量的LABLE的结束地址
*/
.end :
{
*(.__end)
}
// .end代码结束段
_image_binary_end = .;
// _image_binary_end:程序链接结束地址
/*
* Deprecated: this MMU section is used by pxa at present but
* should not be used by new boards/CPUs.
*/
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
/*
* Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
* __bss_base and __bss_limit are for linker only (overlay ordering)
*/
/*
bss段开始,__rel_dyn_start (OVERLAY),表示覆盖__rel_dyn_start指示的位置,
为什么要覆盖__rel_dyn_start指示的段呢?因为此链接脚本生成的代码会在代码段后附加重定位信息段,
当进行代码重定位时需要使用此信息将全局变量、函数地址等使用绝对地址的加上一个偏移地址
(就是连接地址与重定位地址的差值),一旦重定位完成之后,这个附加的动态定位段信息就不在需要了,
因此可以将flash上的bss段直接覆盖到内存的代码段之后,即.rel.dyn段
*/
// .bss_start:bss 段开始地址
.bss_start __rel_dyn_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
// .bss_end:bss段结束地址__bss_base、__bss_limit 都是链接脚本输出的变量,在代码重定位时会用到
// 以下部分未关注
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
好了uboot链接脚本分析到此为止,有大神路过,可以指点一二。