UBoot源码分析1
- 格式:pdf
- 大小:1.03 MB
- 文档页数:28
uboot代码详细分析目录u-boot-1.1.6之cpu/arm920t/start.s分析 (2)u-boot中.lds连接脚本文件的分析 (12)分享一篇我总结的uboot学习笔记(转) (15)U-BOOT内存布局及启动过程浅析 (22)u-boot中的命令实现 (25)U-BOOT环境变量实现 (28)1.相关文件 (28)2.数据结构 (28)3.ENV的初始化 (30)3.1env_init (30)3.2env_relocate (30)3.3env_relocate_spec (31)4.ENV的保存 (31)U-Boot环境变量 (32)u-boot代码链接的问题 (35)ldr和adr在使用标号表达式作为操作数的区别 (40)start_armboot浅析 (42)1.全局数据结构的初始化 (42)2.调用通用初始化函数 (43)3.初始化具体设备 (44)4.初始化环境变量 (44)5.进入主循环 (44)u-boot编译过程 (44)mkconfig文件的分析 (47)从NAND闪存中启动U-BOOT的设计 (50)引言 (50)NAND闪存工作原理 (51)从NAND闪存启动U-BOOT的设计思路 (51)具体设计 (51)支持NAND闪存的启动程序设计 (51)结语 (53)参考文献 (53)U-boot给kernel传参数和kernel读取参数—structtag(以及补充) (53)1、u-boot给kernel传RAM参数 (54)2、Kernel读取U-boot传递的相关参数 (56)3、关于U-boot中的bd和gd (59)U-BOOT源码分析及移植 (60)一、u-boot工程的总体结构: (61)1、源代码组织 (61)2.makefile简要分析 (61)3、u-boot的通用目录是怎么做到与平台无关的? (63)4、smkd2410其余重要的文件: (63)二、u-boot的流程、主要的数据结构、内存分配 (64)1、u-boot的启动流程: (64)2、u-boot主要的数据结构 (66)3、u-boot重定位后的内存分布: (68)三、u-boot的重要细节。
UBOOT源码分析UBOOT是一种开放源码的引导加载程序。
作为嵌入式系统启动的第一阶段,它负责初始化硬件设备、设置系统环境变量、加载内核镜像以及跳转到内核开始执行。
Uboot的源码是开放的,让我们可以深入了解其内部工作机制和自定义一些功能。
Uboot源码的文件组织结构非常清晰,主要分为三个大类:目录、文件和配置。
其中目录包含了一系列相关的文件,文件存放具体的源码实现代码,配置文件包含了针对特定硬件平台的配置选项。
Uboot源码的核心部分是启动代码,位于arch目录下的CPU架构相关目录中。
不同的CPU架构拥有不同的启动代码实现,如arm、x86等。
这些启动代码主要包括以下几个关键功能:1. 初始化硬件设备:Uboot首先需要初始化硬件设备,例如设置时钟、中断控制器、串口等设备。
这些初始化操作是在启动代码中完成的。
通过查看该部分代码,我们可以了解硬件的初始化过程,以及如何配置相关寄存器。
2. 设置启动参数:Uboot启动参数存储在一个称为"bd_info"的数据结构中,它包含了一些关键的设备和内存信息,例如DRAM大小、Flash 大小等。
这些参数是在启动代码中设置的,以便内核启动时能够正确识别硬件情况。
3. 加载内核镜像:Uboot负责加载内核镜像到内存中,以便内核可以正确执行。
在启动代码中,会通过读取Flash设备或者网络等方式,将内核镜像加载到指定的内存地址处。
加载过程中,可能会进行一些校验和修正操作,以确保内核数据的完整性。
4. 启动内核:在内核镜像加载完成后,Uboot会设置一些寄存器的值,并执行一个汇编指令,跳转到内核开始执行。
此时,Uboot的使命即结束,控制权交由内核处理。
除了启动代码,Uboot源码中还包含了许多其他功能模块,如命令行解析器、存储设备驱动、网络协议栈等。
这些功能模块可以根据需求进行配置和编译,以满足不同平台的需求。
例如,可以通过配置文件选择启用一些功能模块,或者自定义一些新的功能。
uboot笔记uboot命令分析+实现uboot笔记:uboot命令分析+实现Ubootuboot命令分析+实现先贴⼀个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令struct cmd_tbl_s {char *name; /* Command Name */int maxargs; /* maximum number of arguments*/int repeatable;/* autorepeat allowed? *//* Implementation function */int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);char *usage; /* Usage message (short)简短⽤法信息*/#ifdef CFG_LONGHELPchar *help; /* Help message (long) 长的帮助信息*/#endif#ifdef CONFIG_AUTO_COMPLETE/* do auto completion on the arguments */ int (*complete)(intargc, char *argv[], charlast_char, intmaxv, char *cmdv[]); #endif};typedefstruct cmd_tbl_s cmd_tbl_t;============================================================uboot的第⼀阶段:硬件相关初始化0.reset执⾏arm920t/start.s 过程如下1.设置cpu svc管理模式2.关看门狗中断,mmu等3.设置时钟,sdram,外部总线4.代码重定位,搬运代码,从flash到sdram5.设置栈,bss段清零, bss⽤于未初始化的全局变量和静态变量6.ldr pc, _start_armboot即进⼊uboot启动的第⼆阶段,调⽤c函数start_armboot()从start_armboot开始经过⼀系列外设初始化⽐如falsh_initnand_init...最后循环调⽤mian_loop()main_loop主要流程{1. ⽣成环境变量mtdparts, 调⽤mtdparts_init2. 在启动过程中若⽆空格键按下则boot_zImage,即run_command(getenv("bootcmd"),0)有空格键按下则run_command("menu",0)3. shell过程,读取⽤户的输⼊并执⾏相应的命令{从控制台获得命令,保存在全局变量comsole_buffer中解析命令⾏字符串,分割命令与参数,最后执⾏run_command(...); }}也就是说在mian_loop中,是处理环境变量和控制台⼈机交互,mian_loop调⽤readline ()读取命令⾏到console_buffer,再把console_buffer复制到lastcommand中去,还要设置flag,最后调⽤run_command (lastcommand, flag)函数,run_command (lastcommand, flag)函数中,⾸先定义cmd_tbl_t *cmdtp,再解析命令⾏。
u_boot移植(五)之分析uboot源码中nand flash操作一、OneNand 和Nand Flash我们已经能从Nand Flash启动了,启动之后,大家会看到如下效果:可以看出,我们的uboot默认使用的是OneNand。
需要注意的是我们的FSC100上面是没有OneNand的,有的是K9F2G08U0B型号的NAND FLASH。
前面我们了解过Nor Flash 和Nand Flash,那OneNand Flash又是什么呢?二、uboot 源码中Nand Flash部分代码分析我们从Nand Flash初始化看起,打开lib_arm/board.c文件,为了紧抓主线,以下代码只列举出了主线代码。
可以看出,我们可以通过CONFIG_CMD_NAND和CONFIG_CMD_ONENAND两个宏来选择NAND FLASH初始化还是 ONENAND FLASH初始化。
uboot 中默认定义了宏CONFIG_CMD_ONENAND,所以选择的是ONENAND FLASH初始化。
我们的FSC100上面使用的是NAND FLASH,所以我们要定义CONFIG_CMD_NAND宏,取消CONFIG_CMD_ONENAND宏的定义。
嗯!先做个记录:修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND。
好了,接下我们看看nand_init()函数时如何实现的。
看以看出,这段代码调用根据CONFIG_SYS_MAX_NAND_DEVICE宏[默认没有定义]的值来决定系统中Nand Flash设备的个数。
接着调用nand_init_chip()函数完成Nand Flash初始化,然后计算出每块Nand Flash的大小。
最终会输出Nand Flash总的容量。
嗯!做个记录:修改include/configs/fsc100.h,定义宏CONFIG_SYS_MAX_NAND_DEVICE,值为1没有看明白的地方是给nand_init_chip()函数传递的参数,接下来我们来看看他们是如何定义的。
uboot源码分析(2)uboot环境变量实现简析uboot 环境变量实现简析----------基于u-boot-2010.03u-boot的环境变量是使⽤u-boot的关键,它可以由你⾃⼰定义的,但是其中有⼀些也是⼤家经常使⽤,约定熟成的,有⼀些是u-boot⾃⼰定义的,更改这些名字会出现错误,下⾯的表中我们列出了⼀些常⽤的环境变量:bootdelay 执⾏⾃动启动的等候秒数baudrate 串⼝控制台的波特率netmask 以太⽹接⼝的掩码ethaddr 以太⽹卡的⽹卡物理地址bootfile 缺省的下载⽂件bootargs 传递给内核的启动参数bootcmd ⾃动启动时执⾏的命令serverip 服务器端的ip地址ipaddr 本地ip 地址stdin 标准输⼊设备stdout 标准输出设备stderr 标准出错设备上⾯只是⼀些最基本的环境变量,请注意,板⼦⾥原本是没有环境变量的,u-boot的缺省情况下会有⼀些基本的环境变量,在你执⾏了saveenv之后,环境变量会第⼀次保存到flash或者eeprom中,之后你对环境变量的修改,保存都是基于保存在flash中的环境变量的操作。
环境变量可以通过printenv命令查看环境变量的设置描述,通过setenv 命令进⾏重新设置,设置完成后可以通过saveenv将新的设置保存在⾮易失的存储设备中(nor flash 、nand flash 、eeprom)。
例如:setenv bootcmd "nand read 0x30008000 0x80000 0x500000;bootm 0x30008000"saveenv通过这两条命令就完成了环境变量bootcmd的重新设置,并讲其保存在固态存储器中。
下⾯简单分析下uboot中环境变量的实现流程。
uboot启动后,执⾏玩start.S中的汇编程序,将跳⼊board.c 中定义的start_arm_boot()函数中,在该函数中,uboot讲完成板⼦外设和相关系统环境的初始化,然后进⼊main_loop循环中进⾏系统启动或者等待与⽤户交互,这其中就包括环境变量的初始化和重定位。
1 U-Boot简介U-Boot,全称Universal BootLoader,是遵循GPL条款的开放源码项目。
从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。
其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。
但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD,VxWorks, QNX, RTEMS, ARTOS,LynxOS嵌入式操作系统。
其目前要支持的目标操作系统是OpenBSD, NetBSD,FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR,VxWorks, LynxOS, pSOS, QNX, RTEMS,ARTOS。
这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。
这两个特点正是U-Boot 项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。
就目前来看,U-Boot 对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。
其它系列的处理器和操作系统基本是在2002年11月PPCBOOT改名为U-Boot后逐步扩充的。
从PPCBOOT向U-Boot 的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心WolfgangDenk[以下简称W.D]本人精湛专业水平和持着不懈的努力。
当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOTLOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。
am335xu-boot启动过程分析 u-boot属于两阶段的bootloader,第⼀阶段的⽂件为 arch/arm/cpu/armv7/start.S 和 arch/arm/cpu/armv7/lowlevel_init.S,前者是平台相关的,后者是开发板相关的。
1. u-boot第⼀阶段代码分析 (1)硬件设备初始化 将CPU的⼯作模式设为管理模式(SVC); 关闭中断; 禁⽤MMU,TLB ; 板级初始化; (2)为加载Bootloader的第⼆阶段代码准备RAM空间 加载u-boot.img,跳转到u-boot.img; 上述⼯作,也就是uboot-spl代码流程的核⼼。
代码如下:arch/arm/cpu/armv7/start.S1/*2 * the actual reset code3*/4reset:5 bl save_boot_params6/*7 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,8 * except if in HYP mode already9*/10 mrs r0, cpsr11 and r1, r0, #0x1f @ mask mode bits12 teq r1, #0x1a @ test for HYP mode13 bicne r0, r0, #0x1f @ clear all mode bits14 orrne r0, r0, #0x13 @ set SVC mode15 orr r0, r0, #0xc0 @ disable FIQ and IRQ16 msr cpsr,r017@@ 以上通过设置CPSR寄存器⾥设置CPU为SVC模式,禁⽌中断18@@ 具体操作可以参考《[kernel 启动流程] (第⼆章)第⼀阶段之——设置SVC、关闭中断》的分析1920/* the mask ROM code should have PLL and others stable */21#ifndef CONFIG_SKIP_LOWLEVEL_INIT22 bl cpu_init_cp1523@@ 调⽤cpu_init_cp15,初始化协处理器CP15,从⽽禁⽤MMU和TLB。
uboot分析之bootmbootm命令执⾏过程中调⽤了bootm_start函数,这个函数⽐较重要,所以先分析它。
mon/cmd_bootm.cCpp代码1. static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])2. {3. void *os_hdr;4. int ret;5. memset ((void *)&images, 0, sizeof (images));//images是⼀个bootm_headers_t类型的全局变量。
见下⾯的分析。
6. images.verify = getenv_yesno ("verify");//从环境变量中检查是否要对镜像的数据(不是镜像头)进⾏校验。
7. bootm_start_lmb();//不做任何有意义的⼯作,除了定义# define lmb_reserve(lmb, base, size)8. /* get kernel image header, start address and length */寻找可⽤的内核镜像,见下⾯的分析。
主要根据传⼊的参数检查镜像的合法性并获取信息。
9. os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,10. &images, &images.os.image_start, &images.os.image_len);//返回指向内存中镜像头的指针11. if (images.os.image_len == 0) {12. puts ("ERROR: can't get kernel image!\n");13. return 1;14. }15. /* get image parameters */16. switch (genimg_get_format (os_hdr)) {//根据镜像魔数获取镜像类型17. case IMAGE_FORMAT_LEGACY:18. images.os.type = image_get_type (os_hdr);//镜像类型19. p = image_get_comp (os_hdr);//压缩类型20. images.os.os = image_get_os (os_hdr);//操作系统类型21. images.os.end = image_get_image_end (os_hdr);//当前镜像的尾地址22. images.os.load = image_get_load (os_hdr);//镜像数据的载⼊地址23. break;24. default:25. puts ("ERROR: unknown image format type!\n");26. return 1;27. }28. /* find kernel entry point */29. if (images.legacy_hdr_valid) {//如果镜像已经通过验证30. images.ep = image_get_ep (&images.legacy_hdr_os_copy);//获取⼊⼝地址,填充images.ep 。
•UBoot源码解析(一)主要内容•分析UBoot是如何引导Linux内核•UBoot源码的一阶段解析BootLoader概念•Boot Loader 就是在操作系统内核运行之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境•通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。
因此,在嵌入式世界里建立一个通用的Boot Loader 几乎是不可能的。
尽管如此,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户特定的Boot Loader 设计与实现。
UBoot来源•U-Boot 是 Das U-Boot 的简称,其含义是 Universal Boot Loader,是遵循 GPL 条款的开放源码项目。
最早德国DENX 软件工程中心的 Wolfgang Denk 基于 8xxROM 和FADSROM 的源码创建了 PPCBoot 工程项目,此后不断添加处理器的支持。
而后,Sysgo Gmbh 把 PPCBoot 移植到 ARM 平台上,创建了 ARMBoot 工程项目。
最终,以 PPCBoot 工程和 ARMBoot 工程为基础,创建了 U-Boot 工程。
•而今,U-Boot 作为一个主流、通用的 BootLoader,成功地被移植到包括 PowerPC、ARM、X86 、MIPS、NIOS、XScale 等主流体系结构上的百种开发板,成为功能最多、灵活性最强,并且开发最积极的开源 BootLoader。
目前。
U-Boot 仍然由 DENX 的 Wolfgang Denk 维护UBoot存储空间分布•UBoot是用来引导OS系统启动,那么它是如何引导OS启动的呢?启动参数内核根文件系统bootloaderUBoot和内核的交互•UBoot如何调用Linux内核?–UBoot通过命令把Linux内核镜像文件从Flash中读取到内存的某一位置,然后设置PC寄存器执向该位置UBoot调用Linux内核的前提条件是?–R0 =0–R1=适当的机器码机器码的位置存放在linux/arch/arm/mach-type 文件中–R2 =启动参数标记列表在内存中的位置–CPU必须设置为SVC模式并关闭中断–MMU必须关闭•UBoot如何给内核传递参数?–UBoot和内核交互是单向的,两个程序不能同时运行,那么要实现参数传递只能通过把参数存放到一个固定内存位置然后调用theKernel函数给对R0,R1,R2寄存器赋值。
Linux启动调用start_kernel函数读取内容即可。
do_bootm_linux函数•do_bootm_linux是UBoot真正启动Linux的函数(位于UBoot所在目录/lib_arm/bootm.c)int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images){bd_t*bd = gd->bd;char*s;int machid = bd->bi_arch_number;void(*theKernel)(int zero, int arch, uint params);..................theKernel = (void (*)(int, int, uint))images->ep; //传递Linux内核首地址theKernel (0, machid, bd->bi_boot_params); //启动Linux内核theKernel函数•参数 zero:设置为0 R0寄存器为0•参数:arch 表示机器存储于R1寄存器中•参数:params用于传递给Linux的参数地址(包括命令行参数信息)(*theKernel)(int zero, int arch, uint params);启动参数•Linux内核启动的时候并不会自己去扫描硬件的基本信息,因为它启动时都已经假设硬件都已经准备好了,但是使用过程中不可避免的要用到硬件信息,这些信息包括内存大小,串口配置信息等等。
那么这信息内核是如何获的呢?–编译Linux内核时可以通过make menuconfig命令进行命令行参数的配置–Uboot引导的时候传递启动参数参数地址给内核。
命令行参数参数信息•u-boot要传递给内核的参包含哪些内容?–MACH_TYPE(即我们所说的机器码)、–命令行参数CommandLine–系统根设备信息(标志,页面大小)、–内存信息(起始地址,大小)、–RAMDISK信息(起始地址,大小)、压缩的RAMDISK根文件系统信息(起始地址,大小)。
由此可见要传递的参数很多tag_list参数列表•UBoot如何组织这么多的参数信息呢?–UBoot使用struct tage 和struct tag_head来描述这些参数信息。
存放位置(include/asm-arm/setup.h)–相对应的在Linux内核源代码中也有完全相同的结构体定义存放位置(linux/arch/arm/include/asm/setup.h)–#define ATAG_CORE0x54410001–#define ATAG_MEM0x54410002–#define ATAG_VIDEOTEXT0x54410003–#define ATAG_RAMDISK0x54410004–#define ATAG_INITRD0x54410005–#define ATAG_SERIAL0x54410006–#define ATAG_REVISION0x54410007–#define ATAG_CMDLINE0x54410009tag结构体struct tag {struct tag_header hdr;union {struct tag_core core;//列表的开始struct tag_mem32mem;//描述内存信息//videotext; //struct tag_videotext videotext;struct tag_ramdisk ramdisk;struct tag_initrd initrd;//串口信息struct tag_serialnr serialnr;serialnr; ////版本信息revision; //struct tag_revision revision;struct tag_videolfb videolfb;//命令行cmdline; //struct tag_cmdline cmdline;。
//内存时钟memclk; //struct tag_memclk memclk;} u;};struct tag_header {u32 size;//每个参数的大小u32 tag;//每个参数的类型};do_bootm_linux流程•在do_bootm_linux函数(Uboot/lib_arm/bootm.c)中定义函数指针theKernel函数指针,并通过读取Linux的内核镜像文件uImage前面的40字节内容获取Linux内核的入口地址–theKernel = (void (*)(int, int, uint))images->ep;•通过getenv函数分别获取机器码和命令行参数信息信息•通过setup_start_tag函数开始参数列表信息•分别通过setup_xxx_tag函数把命令行信息,内存信息,串口信息分别写入参数列表中。
•最后调用cleanup_before_linux函数实现调用Linux前的准备工作•通过调用theKernel函数实现启动Linux内核。
bootm代码argv[], bootm_headers_t **images)int do_bootm_linux(int flag, int argc, char **argv[], bootm_headers_tint do_bootm_linux(int flag, int argc, char{bd_t*bd = gd->bd;char*s;int machid = bd->bi_arch_number;void(*theKernel)(int zero, int arch, uint params);char **commandline = getenv ("bootargs");char。
theKernel = (void (theKernel = (void (**)(int, int, uint))images->ep;s = getenv ("machid");。
setup_start_tag (bd);setup_serial_tag (¶ms);setup_revision_tag (¶ms);setup_memory_tags (bd);setup_commandline_tag (bd, commandline);if (images->rd_start && images->rd_end)setup_initrd_tag (bd, images->rd_start, images->rd_end);setup_videolfb_tag ((gd_tsetup_videolfb_tag ((gd_t **) gd);setup_end_tag (bd);cleanup_before_linux ();theKernel (0, machid, bd->bi_boot_params);return 1;}Linux内核的入口地址•搞清楚UBoot传递给Linux参数机制后,我们又有个新疑问,UBoot是如何知道Linux内核的入口地址呢?毕竟这个时候Linux内核还没启动•启动参数的位于内存具体哪个位置或者说谁决定了启动参数存放的位置?–UBoot工具mkimage的具体作用是什么?–UBoot是不能够直接引导Linux内核镜像文件,必须给内核镜像文件添加一个0x40字节的头部信息这样UBoot才能够引导Linux内核–那么这0x40字节的信息包含哪些内容?mkimage 结构体信息struct mkimage_params {int dflag;int eflag;int fflag;int lflag;int vflag;int xflag;int os;int arch;int type;int comp;char char **dtc;unsigned int addr;unsigned int ep;unsigned int ep; //Linux//Linux 内核镜像的入口地址char char **imagename;char char **datafile;char char **imagefile;char char **cmdname;};•mkimage 程序通过-e 参数写入Linux 内核的镜像入口地址,而这入口地址被以结构体的形式写入镜像文件的头部0x40字节的位置中,UBoot 利用image_get_ep 函数从内核文件中读取入口地址UBoot 源码结构开发使用文档文档Doc存放制作S-Record 或者 U-Boot 格式的映像等工具,例如mkimage 工具tools 一些独立运行的应用程序的例子,例如helloworld应用例程examples 数字温度测量器或者传感器的驱动通用Dtt RTC 的驱动程序通用Rtc 硬盘接口程序通用Disk 通用的设备驱动程序,主要有以太网接口的驱动通用drivers 存放上电自检程序通用Post 存放文件系统的程序通用Fs 存放网络的程序通用Net 通用库函数的实现通用lib_generic 通用的多功能函数实现通用common 头文件和开发板配置文件,所有开发板的配置文件都在configs 目录下通用include 存放对X86体系结构通用的文件,主要用于实现X86平台通用的函数平台依赖lib_i386存放对ARM 体系结构通用的文件,主要用于实现ARM 平台通用的函数平台依赖lib_arm Uboot 头文件平台依赖include 存放CPU 相关的目录文件,例如:mpc8xx 、ppc4xx 、arm720t 、arm920t 、 xscale 、i386等目录平台依赖cpu 存放电路板相关的目录文件,例如:RPXlite(mpc8xx)、smdk2410(arm920t)、sc520_cdp(x86) 等目录平台依赖board 解 释 说 明特 性目 录UBoot入口位置•UBoot是如何对编译好的二进制文件进行组合链接成一个可执行文件的?–lds链接文件用于描述如何组织和编译这些.o文件。