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

2019-07-13 08:27发布

酝酿许久,关于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-r0archarmcpuu-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-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链接脚本分析到此为止,有大神路过,可以指点一二。