Uboot链接脚本与启动流程
imx6ull uboot链接脚本为u-boot.lds,对链接脚本进行简要分析:
u-boot.lds
1 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") 2 OUTPUT_ARCH(arm) 3 ENTRY(_start) // 入口函数,该函数在verctor.S中定义 4 SECTIONS 5 { 6 . = 0x00000000; 7 . = ALIGN(4); 8 .text : // 代码段 9 { 10 *(.__image_copy_start) // 0x87800000,整个镜像的起始地址,拷贝起始地址 11 *(.vectors) // 0x87800000,存放中断向量表 12 arch/arm/cpu/armv7/start.o (.text*) // start.c 13 *(.text*) // .text段结束 14 } 15 . = ALIGN(4); 16 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } 17 . = ALIGN(4); 18 .data : { 19 *(.data*) 20 } 21 . = ALIGN(4); 22 . = .; 23 . = ALIGN(4); 24 .u_boot_list : { 25 KEEP(*(SORT(.u_boot_list*))); 26 } 27 . = ALIGN(4); 28 .image_copy_end : // 镜像拷贝结束,0x8784f1a4 29 { 30 *(.__image_copy_end) 31 } 32 .rel_dyn_start : // rel段起始地址0x8784f1a4 33 { 34 *(.__rel_dyn_start) 35 } 36 .rel.dyn : { 37 *(.rel*) 38 } 39 .rel_dyn_end : // rel段结束地址0x8785794c 40 { 41 *(.__rel_dyn_end) 42 } 43 .end : 44 { 45 *(.__end) 46 } 47 _image_binary_end = .; // 0x8785794c 48 . = ALIGN(4096); 49 .mmutable : { 50 *(.mmutable) 51 } 52 .bss_start __rel_dyn_start (OVERLAY) : { // bss段起始地址:0x8784f1a4 53 KEEP(*(.__bss_start)); 54 __bss_base = .; 55 } 56 .bss __bss_base (OVERLAY) : { 57 *(.bss*) 58 . = ALIGN(4); 59 __bss_limit = .; 60 } 61 .bss_end __bss_limit (OVERLAY) : { // bss段结束地址0x8789a194 62 KEEP(*(.__bss_end)); 63 } 64 .dynsym _image_binary_end : { *(.dynsym) } 65 .dynbss : { *(.dynbss) } 66 .dynstr : { *(.dynstr*) } 67 .dynamic : { *(.dynamic*) } 68 .plt : { *(.plt*) } 69 .interp : { *(.interp*) } 70 .gnu.hash : { *(.gnu.hash) } 71 .gnu : { *(.gnu*) } 72 .ARM.exidx : { *(.ARM.exidx*) } 73 .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
vector.S
_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
在u-boot.map中有内存配置部分:
Name Origin Length Attributes *default* 0x0000000000000000 0xffffffffffffffff Linker script and memory map Address of section .text set to 0x87800000 0x0000000000000000 . = 0x0 0x0000000000000000 . = ALIGN (0x4) .text 0x0000000087800000 0x3cd64 *(.__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
函数执行流程:
reset ->save_boot_params ->save_boot_params_ret // 将处理器切成svc模式,并关闭FIQ和IRQ,设置中断向量偏移 ->cpu_init_cp15 // 设置cp15协处理器 ->cpu_init_crit ->lowlevel_init // 设置sp指针,r9寄存器 ->s_init(空函数) ->_main
save_boot_params_ret函数作用:将处理器切成svc模式,并关闭FIQ和IRQ,设置中断向量偏移
mrs r0, cpsr and r1, r0, #0x1f @ mask mode bits teq r1, #0x1a @ test for HYP mode bicne r0, r0, #0x1f @ clear all mode bits orrne r0, r0, #0x13 @ set SVC mode orr r0, r0, #0xc0 @ disable FIQ and IRQ msr cpsr,r0 mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register bic r0, #CR_V @ V = 0,将V清零可以重定位中断向量表 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register /* Set vector address in CP15 VBAR register */ ldr r0, =_start // 代码起始的地址,也是中断向量表起始的地方 mcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 bl cpu_init_crit
lowlevel_init.S
ldr r0, =_start // 代码起始的地址,也是中断向量表起始的地方 mcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 bl cpu_init_crit lowlevel_init.S ENTRY(lowlevel_init) /* * Setup a temporary stack. Global data is not available yet. */ ldr sp, =CONFIG_SYS_INIT_SP_ADDR // 先让sp指针指向内部ram,0x91ff00,imx6ull内部ram地址为0x900000-0x91ffff bic sp, sp, #7 /* 8-byte alignment for ABI compliance,八字节对齐 */ #ifdef CONFIG_SPL_DM mov r9, #0 #else /* * Set up global data for boards that still need it. This will be * removed soon. */ #ifdef CONFIG_SPL_BUILD ldr r9, =gdata #else sub sp, sp, #GD_SIZE bic sp, sp, #7 mov r9, sp // 0x91fe08 #endif #endif /* * Save the old lr(passed in ip) and the current lr to stack */ push {ip, lr} /* * Call the very early init function. This should do only the * absolute bare minimum to get started. It should not: * * - set up DRAM * - use global_data * - clear BSS * - try to start a console * * For boards with SPL this should be empty since SPL can do all of * this init in the SPL board_init_f() function which is called * immediately after this. */ bl s_init pop {ip, pc} ENDPROC(lowlevel_init)
crt0.S
ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) #else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif mov r0, sp bl board_init_f_alloc_reserve // 传入r0参数,也就是sp指针 mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 // r9保存的就是gd指针,也就是 gd 指向 0X0091FA00 bl board_init_f_init_reserve mov r0, #0 bl board_init_f // 初始化一系列外设,初始化gd的各个变量 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code // 重定位代码 here: /* * now relocate vectors */ bl relocate_vectors // 重定位中断向量表 /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) # ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */ bl spl_relocate_stack_gd cmp r0, #0 movne sp, r0 movne r9, r0 # endif ldr r0, =__bss_start /* this is auto-relocated! */ #ifdef CONFIG_USE_ARCH_MEMSET ldr r3, =__bss_end /* this is auto-relocated! */ mov r1, #0x00000000 /* prepare zero to clear BSS */ subs r2, r3, r0 /* r2 = memset len */ bl memset #else ldr r1, =__bss_end /* this is auto-relocated! */ mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ #if defined(CONFIG_CPU_V7M) itt lo #endif strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l #endif #if ! defined(CONFIG_SPL_BUILD) bl coloured_LED_init bl red_led_on #endif /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ /* call board_init_r */ #if defined(CONFIG_SYS_THUMB_BUILD) ldr lr, =board_init_r /* this is auto-relocated! */ // 代码重定位和初始化一些外设 bx lr #else ldr pc, =board_init_r /* this is auto-relocated! */ #endif /* we should not return here. */ #endif ENDPROC(_main) ulong board_init_f_alloc_reserve(ulong top) { /* Reserve early malloc arena */ #if defined(CONFIG_SYS_MALLOC_F) top -= CONFIG_SYS_MALLOC_F_LEN; // CONFIG_SYS_MALLOC_F_LEN定义的也就是uboot早期的malloc内存池的大小,sp减去400字节大小 #endif /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ top = rounddown(top-sizeof(struct global_data), 16); return top; } void board_init_f_init_reserve(ulong base) { struct global_data *gd_ptr; #ifndef _USE_MEMCPY int *ptr; #endif /* * clear GD entirely and set it up. * Use gd_ptr, as gd may not be properly set yet. */ gd_ptr = (struct global_data *)base; /* zero the area */ #ifdef _USE_MEMCPY memset(gd_ptr, '\0', sizeof(*gd)); #else for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); ) *ptr++ = 0; #endif /* set GD unless architecture did it already */ #if !defined(CONFIG_ARM) arch_setup_gd(gd_ptr); #endif /* next alloc will be higher by one GD plus 16-byte alignment */ base += roundup(sizeof(struct global_data), 16); /* * record early malloc arena start. * Use gd as it is now properly set for all architectures. */ #if defined(CONFIG_SYS_MALLOC_F) /* go down one 'early malloc arena' */ gd->malloc_base = base; // malloc_base = 0X0091FB00 /* next alloc will be higher by one 'early malloc arena' size */ base += CONFIG_SYS_MALLOC_F_LEN; #endif }
在imx6ull内存布局中,也就是在典型的内存布局中,通常会将 .rodata 段放在 .text 段之后,而 bss 段则会放在 .rodata 段之后。简单来说,内存布局的顺序通常是 .text -> .rodata -> bss,所以通常情况下,计算代码长度时可以使用__bss_end - _start这种方法
uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 内核 , 这 个 功 能 就 是 由 run_main_loop 函 数 来 完 成 的
cli_loop 函数是 uboot 的命令行处理函数,我们在 uboot 中输入各种命令,进行各种操作就是有 cli_loop 来处理的,此函数定义在文件 common/cli.c 中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通