1、背景:
由于项目需求,需要了解bootloader的相关知识,而项目中使用到的bootloader为u-boot-2009.08,
所以本文以u-boot的2009.08版本为基础进行分析。
正如标题所言,本文只对u-boot的编译过程进行分析,而暂且不去理会u-boot代码结构、u-boot的
启动流程以及详细的代码分析等。
写作本文的目的主要是为了方便日后查阅u-boot相关的知识,正所谓“好记性不如烂笔头”。另外
,
如果此文能帮助各位同行或者刚接触
u-boot的人更好的理解u-boot的相关知识,本人也深感欣慰。如
文中有不当或者错误的地方,也请指正,本人不胜感激!
2、u-boot
u-boot是bootloader中的一种,也是嵌入式开发中应用最广的一种,已经支持了多种CPU和多种开发
板。u-boot在嵌入式产品中的作用主要是引导操作系统,即板子上电或者复位之后首先会运行u-boot程序
,然后才去加载操作系统。需要强调的是,这只是u-boot最平凡的作用,u-boot本身的“能力”远不止于此。
注:不是所有的板子上电后运行的第一段程序就是u-boot程序,很多CPU内部集成了一块ROM,出
厂时就由厂家固化了一段引导程序,用户是擦除不掉的。这段程序是在u-boot之前运行的。这块ROM程序
存在的原因:CPU上电后需要运行程序,假设程序在nand flash或者usb又或者SD卡这样的存储设备中,
而CPU的程序需要在RAM里面运行,这时就由这段ROM程序驱动nand flash或者usb又或者SD卡接口,将
其中的程序拷贝到RAM里面再去执行。很多板子在没有任何程序(也没有u-boot程序)的时候能用USB进
行烧片应该也是由于这个原因。
u-boot-2009.08是uboot的一个版本,u-boot的命名方式可以网上查找相关资料了解。
3、u-boot编译流程概要
回到本文的主题----u-boot-2009.08的编译过程分析
。
一般编译u-boot分为两步:
(1)执行命令make boardname_config配置开发板,如make smdk2410_config;
(2)执行命令make进行编译;
3.1、make boardname_config流程概要
执行make boardname_config命令后,根据Makefile指定的编译顺序,会执行以下几个操作:
<1>、在顶层include目录下:生成asm目录,并将此目录链接到对应的以asm-开头的目录,如ln
-s
asm-arm asm;
<2>、在顶层include目录下:在对应的以asm-开头的目录中生成arch目录,并将此目录链接到对应
的以arch-开头的目录,如ln -s arch-s3c24x0 asm-arm/arch;
<3>、在顶层include目录下:生成config.mk文件,并在此文件中添加相应的内容;
<4>、在顶层include目录下:生成config.h文件,并在此文件中添加相应的内容。
3.2、make流程概要
执行make命令后,根据Makefile指定的编译顺序,会执行以下一些操作:
<1>、在顶层include目录下:生成timestamp_autogenerated.h文件;
<2>、在顶层include目录下:生成version_autogenerated.h文件;
<3>、在顶层include目录下:生成autoconf.mk文件,并在此文件中添加相应的内容;
<4>、在tools、example/standalone和example/api三个目录下根据各自的Makefile文件进行编译。在
tools目录下会生成mkimage、img开头的一些工具。其他两个目录下生成一些测试demo,如helloworld;
<5>、在顶层cpu目录下:在对应的具体的CPU型号的目录中根据Makefile文件生成start.o文件。某些
CPU下还会生成start16.o等目标文件;
<6>、在对应的目录下生成多个静态库文件,如在lib_generic目录下生成libgeneric.a、liblzma.a、
liblzo.a;在drivers/i2c目录下生成libi2c.a;在common目录下生成libcommon.a等;
<7>、在
顶层board目录下对应的目录中生成对应的库文件,如在board/samsung/smdk2410目录下生
成
libsmdk2410.a
;
<8>、在
顶层目录下生成u-boot.lds链接脚本
;
<9>、在顶层目录下生成u-boot.map文件和ELF格式的文件u-boot ;
<10>、在顶层目录下分别生成S-recordd格式文件u-boot.srec 、二进制文件u-boot.bin和system.map
文件;
<11>、根据配置,在顶层目录下还可能会生成u-boot-nand.bin和u-boot-onenand.bin二进制文件;
4、u-boot-2009.08顶层Makefile分析
不管执行make boardname_config命令还是make命令,都是根据Makefile文件进行相关处理。所以,
首先我们还是应该仔细阅读清楚Ma
kefile文件。下面便正式开始顶层Makefile文件的分析:
注:为了方便阅读,u-boot源码大部分标注为黑色,需要注意的地方标注为红色;注释标注为蓝色,
需要注意的地方也标注为红色;
/***********************************************************************************************************************/
/*********************************************Start:Makefile文件分析**********************************************/
/***********************************************************************************************************************/
/* 24~27行定义了u-boot的各版本号 */
24 VERSION = 2009
25 PATCHLEVEL = 08
26 SUBLEVEL =
27 EXTRAVERSION =
25 PATCHLEVEL = 08
26 SUBLEVEL =
27 EXTRAVERSION =
/* 28~32行:如果变量SUBLEVEL的值为不空,则执行ifneq段,否则执行else段,显然为else段*/
28 ifneq "$(SUBLEVEL)" ""
29 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
30 else
31 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
32 endif
28 ifneq "$(SUBLEVEL)" ""
29 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
30 else
31 U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL)$(EXTRAVERSION)
32 endif
/* 33~34行定义了变量TIMESTAMP_FILE和变量VERSION_FILE */
/* 注意:下面源码中标红的两个文件为编译过程中自动生成的,编译之前是不存在的,$(obj)此处还未定
义,为空,所以这两个文件最后生成的位置就是顶层目录下的include目录中。这两个变量是如何生成的,
暂且记为问题Q1和Q2,留到后面再分析,先按照顺序往下接着走 */
33 TIMESTAMP_FILE =$(obj)include/t imestamp_autogenerated.h(Q1:timestamp_autogenerated.h如何生成的?)
34 VERSION_FILE =$(obj)include/ version_autogenerated.h(Q2:version_autogenerated.h如何生成的?)
33 TIMESTAMP_FILE =$(obj)include/t imestamp_autogenerated.h(Q1:timestamp_autogenerated.h如何生成的?)
34 VERSION_FILE =$(obj)include/ version_autogenerated.h(Q2:version_autogenerated.h如何生成的?)
/* 36~43行定义了变量HOSTARCH ,即主机的架构,得到的结果为HOSTARCH := i386 */
36
HOSTARCH := $(shell uname -m | \
/* $(shell command)格式的意思是在Makefile文件中调用shell命令
37 sed -e s/i.86/i386/ \ uname -m为打印主机硬件架构名字的命令,在我的机器上为i686 */
38 -e s/sun4u/sparc64/ \ /* 符号“|” 为连接管道命令 */
39 -e s/arm.*/arm/ \ /* 符号“\” 为断行符 */
40 -e s/sa110/arm/ \ /* sed为操作字符的管道命令,sed -e s/aaa/bbb/命令表示把字符串
41 -e s/powerpc/ppc/ \ 中的字符aaa替换为bbb*/
42 -e s/ppc64/ppc/ \ /* i.86中的.表示任意字符 */
43 -e s/macppc/ppc/)
/* 45~46行定义了变量HOSTOS ,即主机的操作系统,得到的结果为HOSTOS := linux */
45 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ /*uname -m为打印主机操作系统内核名称,在我的
46 sed -e 's/\(cygwin\).*/cygwin/') Ubuntu系统中为Linux */
37 sed -e s/i.86/i386/ \ uname -m为打印主机硬件架构名字的命令,在我的机器上为i686 */
38 -e s/sun4u/sparc64/ \ /* 符号“|” 为连接管道命令 */
39 -e s/arm.*/arm/ \ /* 符号“\” 为断行符 */
40 -e s/sa110/arm/ \ /* sed为操作字符的管道命令,sed -e s/aaa/bbb/命令表示把字符串
41 -e s/powerpc/ppc/ \ 中的字符aaa替换为bbb*/
42 -e s/ppc64/ppc/ \ /* i.86中的.表示任意字符 */
43 -e s/macppc/ppc/)
/* 45~46行定义了变量HOSTOS ,即主机的操作系统,得到的结果为HOSTOS := linux */
45 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ /*uname -m为打印主机操作系统内核名称,在我的
46 sed -e 's/\(cygwin\).*/cygwin/') Ubuntu系统中为Linux */
/*tr '[:upper:]' '[:lower:]'命令将大写字母替换为小写字母 */
/* 48~51行定义了变量 SHELL ,得到的结果为 SHELL := /bin/bash */
48 # Set shell to bash if possible, otherwise fall back to sh
49 SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
50 else if [ -x /bin/bash ]; then echo /bin/bash; \
51 else echo sh; fi; fi)
/* 53行将上述三个变量导出,以便子Makefile中也可以访问这三个变量 */
53 export HOSTARCH HOSTOS SHELL
/* 56行的变量VENDOR暂时没有定义 */
55 # Deal with colliding definitions from tcsh etc.
56 VENDOR=
56 VENDOR=
/* 58~64行定义XECHO变量,得到的结果是XECHO =: */
58 #########################################################################
59 # Allow for silent builds /* 静态编译,即make -s,此时编译过程中将不打印出相关 的命令 */
59 # Allow for silent builds /* 静态编译,即make -s,此时编译过程中将不打印出相关 的命令 */
60
ifeq (,$(findstring s,$(MAKEFLAGS)))
/*findstring 函数在变量MAKEFLAGS中查找是否存在字符s */
61 XECHO = echo
62 else
63 XECHO = :
64 endif
61 XECHO = echo
62 else
63 XECHO = :
64 endif
/* 68~86行说明了在编译u-boot时可以用两种方式制定一个输出路径。u-boot的源码顶层目录下的
README文件中也对此有说明。对此不再做详细说明,一般情况下都不指定,而是直接编译 */
66 #########################################################################
67 #
68 # U-boot build supports producing a object files to the separate external
69 # directory. Two use cases are supported:
70 #
71 # 1) Add O= to the make command line
72 # 'make O=/tmp/build all'
73 #
74 # 2) Set environement variable BUILD_DIR to point to the desired location
75 # 'export BUILD_DIR=/tmp/build'
76 # 'make'
77 #
78 # The second approach can also be used with a MAKEALL script
79 # 'export BUILD_DIR=/tmp/build'
80 # './MAKEALL'
81 #
82 # Command line 'O=' setting overrides BUILD_DIR environent variable.
83 #
84 # When none of the above methods is used the local build is performed and
85 # the object files are placed in the source directory.
86 #
/* 88~92行根据具体情况对变量BUILD_DIR 赋值 */
88 ifdef O
89 ifeq ("$(origin O)", "command line") /*origin 函数查询变量O的出处,如果O是在命令行中定义的话,则
67 #
68 # U-boot build supports producing a object files to the separate external
69 # directory. Two use cases are supported:
70 #
71 # 1) Add O= to the make command line
72 # 'make O=/tmp/build all'
73 #
74 # 2) Set environement variable BUILD_DIR to point to the desired location
75 # 'export BUILD_DIR=/tmp/build'
76 # 'make'
77 #
78 # The second approach can also be used with a MAKEALL script
79 # 'export BUILD_DIR=/tmp/build'
80 # './MAKEALL'
81 #
82 # Command line 'O=' setting overrides BUILD_DIR environent variable.
83 #
84 # When none of the above methods is used the local build is performed and
85 # the object files are placed in the source directory.
86 #
/* 88~92行根据具体情况对变量BUILD_DIR 赋值 */
88 ifdef O
89 ifeq ("$(origin O)", "command line") /*origin 函数查询变量O的出处,如果O是在命令行中定义的话,则
90
BUILD_DIR := $(O)
返回command line */
91 endif
92 endif
/* 94~103行根据变量BUILD_DIR的值做如下处理 */
94 ifneq ($(BUILD_DIR),) /*如果变量BUILD_DIR不为空将变量BUILD_DIR的值保存到saved-
95 saved-output := $(BUILD_DIR) output中*/
96
97 # Attempt to create a output directory.
98 $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})/* 如果不存在目录BUILD_DIR,则用mkdir命令创建*/
99
100
91 endif
92 endif
/* 94~103行根据变量BUILD_DIR的值做如下处理 */
94 ifneq ($(BUILD_DIR),) /*如果变量BUILD_DIR不为空将变量BUILD_DIR的值保存到saved-
95 saved-output := $(BUILD_DIR) output中*/
96
97 # Attempt to create a output directory.
98 $(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})/* 如果不存在目录BUILD_DIR,则用mkdir命令创建*/
99
100