学习目标:
uboot链接脚本分析
学习内容:
学习使用了正点原子的I.MX6ULL教程及开发平台。
uboot的链接脚本u-boot.lds,u-boot.map。
学习时间:
2022-07-17
学习产出:
分析uboot的启动流程之前,首先要找到uboot的入口函数,第一个运行的函数在哪里。uboot编译完之后都是需要通过链接脚本来指定程序如何存放。所以可以通过链接脚本找到uboot的入口函数。
uboot在编译完成后会在uboot的根目录下生成一个名为uboot.lds的链接脚本文件,此文件是根据arch/arm/cpu/u-boot.lds文件,经过uboot编译后生成的,uboot在编译时,会根据原本的链接脚本文件增加一些东西,才最终在uboot的根目录下生成u-boot.lds最终使用的链接脚本文件。在uboot编译时,会打印如下链接命令:
arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic
-Ttext 0x87800000 -o u-boot -T u-boot.lds
arch/arm/cpu/armv7/start.o --start-group
arch/arm/cpu/built-in.o
arch/arm/cpu/armv7/built-in.o
arch/arm/imx-common/built-in.o
arch/arm/lib/built-in.o
board/freescale/common/built-in.o
board/freescale/mx6ullevk/built-in.o
cmd/built-in.o
common/built-in.o
disk/built-in.o
drivers/built-in.o
drivers/dma/built-in.o
drivers/gpio/built-in.o
drivers/i2c/built-in.o
drivers/mmc/built-in.o
drivers/mtd/built-in.o
drivers/mtd/onenand/built-in.o
drivers/mtd/spi/built-in.o
drivers/net/built-in.o
drivers/net/phy/built-in.o
drivers/pci/built-in.o
drivers/power/built-in.o
drivers/power/battery/built-in.o
drivers/power/fuel_gauge/built-in.o
drivers/power/mfd/built-in.o
drivers/power/pmic/built-in.o
drivers/power/regulator/built-in.o
drivers/serial/built-in.o
drivers/spi/built-in.o
drivers/usb/dwc3/built-in.o
drivers/usb/emul/built-in.o
drivers/usb/eth/built-in.o
drivers/usb/gadget/built-in.o
drivers/usb/gadget/udc/built-in.o
drivers/usb/host/built-in.o
drivers/usb/musb-new/built-in.o
drivers/usb/musb/built-in.o
drivers/usb/phy/built-in.o
drivers/usb/ulpi/built-in.o
fs/built-in.o
lib/built-in.o
net/built-in.o
test/built-in.o
test/dm/built-in.o
--end-group arch/arm/lib/eabi_compat.o
-L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/4.9.4 -lgcc -Map u-boot.map
这些就是根据链接脚本u-boot.lds执行链接的过程,由此可知链接地址为0x87800000。
uboot根目录中链接脚本的内容如下:
#指定输出可执行文件是elf格式,32位ARM指令,小端模式
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)#指定输出而执行文件运行于ARM平台
ENTRY(_start)#程序默认的入口函数为_start
SECTIONS
{
. = 0x00000000;#指明目标代码起始地址为0x0,"."代表当前位置
. = ALIGN(4);#按4字节对齐
.text :#.text代码段,可读可执行
{
*(.__image_copy_start)#uboot映像文件复制起始地址,在u-boot.map中定义
*(.vectors)#中断向量表
arch/arm/cpu/armv7/start.o (.text*)#start.o文件
*(.text*)#其他代码段
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }#只读数据段RO段(存放常量)
. = ALIGN(4);
.data : {#读写数据段 RW段(存放已经初始化的全局变量和静态变量)
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
.u_boot_list : {#uboot.list段
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN(4);
.image_copy_end :#uboot映像文件复制结束地址,在u-boot.map中定义
{
*(.__image_copy_end)
}
.rel_dyn_start :
{
*(.__rel_dyn_start)
}
.rel.dyn : {
*(.rel*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .;
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
.bss_start __rel_dyn_start (OVERLAY) : {#bss段起始地址
KEEP(*(.__bss_start));
__bss_base = .;
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {#bss段结束地址
KEEP(*(.__bss_end));
}
.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.map文件是uboot的映射文件,从此文件中可以看出某个文件或者函数被链接到了哪个地址,文件部分内容如下:
名称 来源 长度 属性
*default* 0x0000000000000000 0xffffffffffffffff
链结器命令稿和内存映射
段 .text 的地址设置为 0x87800000
0x0000000000000000 . = 0x0
0x0000000000000000 . = ALIGN (0x4)
.text 0x0000000087800000 0x3f93c
*(.__image_copy_start)
.__image_copy_start
0x0000000087800000 0x0 arch/arm/lib/built-in.o
0x0000000087800000 __image_copy_start
*(.vectors)
.vectors 0x0000000087800000 0x300 arch/arm/lib/built-in.o
0x0000000087800000 _start
0x0000000087800020 _undefined_instruction
0x0000000087800024 _software_interrupt
0x0000000087800028 _prefetch_abort
0x000000008780002c _data_abort
0x0000000087800030 _not_used
0x0000000087800034 _irq
0x0000000087800038 _fiq
0x0000000087800040 IRQ_STACK_START_IN
arch/arm/cpu/armv7/start.o(.text*)
.text 0x0000000087800300 0xb0 arch/arm/cpu/armv7/start.o
0x0000000087800300 reset
0x0000000087800304 save_boot_params_ret
0x0000000087800340 c_runtime_cpu_setup
0x0000000087800350 save_boot_params
0x0000000087800354 cpu_init_cp15
0x00000000878003a8 cpu_init_crit
*(.text*)
.text 0x00000000878003b0 0x24 arch/arm/cpu/armv7/built-in.o
0x00000000878003b0 lowlevel_init
.text.v7_maint_dcache_all
0x00000000878003d4 0xcc arch/arm/cpu/armv7/built-in.o
.text.invalidate_icache_all
0x00000000878004a0 0x18 arch/arm/cpu/armv7/built-in.o
0x00000000878004a0 invalidate_icache_all
.text.v7_outer_cache_enable
0x00000000878004b8 0x4 arch/arm/cpu/armv7/built-in.o
0x00000000878004b8 v7_outer_cache_enable
.text.v7_outer_cache_disable
0x00000000878004bc 0x4 arch/arm/cpu/armv7/built-in.o
0x00000000878004bc v7_outer_cache_disable
.text.v7_outer_cache_flush_all
0x00000000878004c0 0x4 arch/arm/cpu/armv7/built-in.o
0x00000000878004c0 v7_outer_cache_flush_all
.text.flush_dcache_all
0x00000000878004c4 0x14 arch/arm/cpu/armv7/built-in.o
0x00000000878004c4 flush_dcache_all
从中可以看出__image_copy_start的地址为0x87800000,入口函数_start位于文件arch/arm/lib/vectors.S文件中,此文件内容如下:
#include <config.h>
.globl _start
.section ".vectors", "ax"
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_start 后面就是中断向量表,从图中的“.section “.vectors”,"ax”可以得到,此代码存放在.vectors 段里面。
从u-boot.map文件中可以看出一些变量值,如下:
__image_copy_start 0x87800000 uboot 拷贝的首地址
__image_copy_end 0x87853728 uboot 拷贝的结束地址
__rel_dyn_start 0x87853728 .rel.dyn 段起始地址
__rel_dyn_end 0x8785c3c8 .rel.dyn 段结束地址
image_binary_end 0x8785c3c8 镜像结束地址
__bss_start 0x87853728 .bss 段起始地址
__bss_end 0x8789e814 .bss 段结束地址
除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化,如果修改了 uboot 代码、修改了 uboot 配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准!