imx6q 平台2016版 u-boot链接脚本详解

酝酿许久,关于uboot终于开篇;因为最近一直在使用imx6q平台进行Linux应用开发,但是我对imx6q的外围不是很熟悉,而且系统也不是我从头搭建的,总感觉心里没底,这样做事不是我的风格;所以有点空闲之后,决定把imx6平台的uboot、Linux整体再捋一遍,这样我睡觉都踏实一点;同时把之前的uboot、Linux学习应用笔记也搬到博客上来,巩固自己所学知识,好让自己快点将嵌入式Linux的知识由点扩展到面,达到熟练程度。
继承我学习Cortex-m3/m4系列的方法,我一般要弄清楚程序上电后的启动位置、程序在内存的布局、中断向量分布等。本来在很久以前我就系统的学习过uboot,主要是ATMEL的9G25平台,也写过一些笔记,但是在捋2016-03这一版的uboot时,还是有一些不一样的地方;比如代码重定位、由此衍生的位置无关代码、链接脚本等;因此决定将uboot分为链接脚本分析、代码启动流程分析、uboot命令实现、uboot与具体的硬件驱动移植4个部分来写。
好吧,以下就是我写的uboot链接脚本分析
链接脚本目录:\u-boot-2016.03-r0\arch\arm\cpu\u-boot.lds

uboot 链接脚本分析

这份链接脚本分析中省略了关于安全段的链接信息

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-r0\arch\arm\cpu\armv7\start.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-r0\arch\arm\lib\sections.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-r0\arch\arm\lib\vectors.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链接脚本分析到此为止,有大神路过,可以指点一二。

猜你喜欢

转载自blog.csdn.net/zhaoyun_zzz/article/details/84895264