配置过程分析
make 100ask24x0_config
相当于执行(Makefile分析)
MKCONFIG := $(SRCTREE)/mkconfig
.........
SRCTREE := $(CURDIR) /*CURDIR为当前目录*/
.........
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
假定在 U-Boot-1.1.6 的根目录下编译,则其中的 MKCONFIG 就是根目录下的 mkconfig
文件。$(@:_config=)的结果就是将“100ask24x0_config”中的“_config”去掉,结果为“100ask24x0”。
所以“make 100ask24x0_config”实际上就是执行如下命令
./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
# $0 $1 $2 $3 $4 $5 $6
再来看看 mkconfig 的作用,在 mkconfig 文件开头第 6 行给出了它的用法
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
分步骤分析 mkconfig 的作用
1、确定开发板名称 BOARD_NAME,相关代码如下
对于“./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0”命令,其中没有“–”、
“-a”、“-n”等符号,所以第 14~22 行没做任何事情。第 11、12 行两个变量仍维持原来的值。
执行完第 23 行后,BOARD_NAME 的值等于第 1 个参数,即“100ask24x0 ”
2、创建到平台/开发板相关的头文件的链接
略过 mkconfig 文件中的一些没有起作用的行,如下所示
第 33 行判断源代码目录和目标文件目录是否一样,可以选择在其他目录下编译 U-Boot,这可以令源代码目录保持干净,可以同时使用不同的配置进行编译。不过本书是直接在源代码目录下编译的,第 33 行的条件不满足,将执行 else 分支的代码。
第 46~48 行进入 include 目录,删除 asm 文件(这是上一次配置时建立的链接文件),
然后再次建立 asm 文件,并令它链接向 asm-$2 目录,即 asm-arm
继续往下看代码:
第 51 行删除 asm-$2/arch 目录,即 asm-arm/arch。
对于“./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0”命令,$6 为“s3c24x0”,不为空,也不是“NULL”,所以第 53 行的条件不满足,将执行 else 分支。
第 56 行中,LNPREFIX 为空,所以这个命令实际上就是“ln -s arch-$6 asm-$2/arch”,即“ln -s arch-s3c24x0 asm-arm/arch”。
第 60、61 行重新建立 asm-arm/proc 文件,并让它链接向 proc-armv 目录。
3、创建顶层 Makefile 包含的文件 include/config.mk,如下所示:
# > 表示新建一个config.mk文件
# >> 表示追加
对于“ ./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0”命令,上面几行代码
创建的 config.mk 文件内容如下:
config.mk效果:
#ARCH = arm
#CPU = arm920t
#BOARD = 100ask24x0
#SOC = s3c24x0
4、创建开发板相关头文件include/config.h,如下图所示:
APPEND 维持原值“no”,所以 config.h 被重新建立,它的内容如下
#config.h的内容
# /* Automatically generated - do not edit */
# #include <configs/$1.h>
现在总结一下,配置命令“make 100ask24x0_config”,实际的作用就是执行“./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0”命令。假设执行“./mkconfig $1 $2 $3 $4 $5 $6”命令,则将产生如下结果。
(1)开发板名称 BOARD_NAME 等于$1。
(2)创建到平台/开发板相关的头文件的链接,如下所示
(3)创建顶层 Makefile 包含的文件 include/config.mk,如下所示
(4)创建开发板相关的头文件 include/config.h,如下所示
从这 4 个结果可以知道,如果要在 board 目录下新建一个开发板<board_name>的目录,则在 include/configs 目录下也要建立一个文件<board_name>.h,里面存放的就是开发板<board_name>的配置信息。
U-Boot 还没有类似 Linux 一样的可视化配置界面(比如使用 make menuconfig 来配置),要手动修改配置文件 include/config/<board_name>.h 来裁减、设置 U-Boot编译过程分析(Makefile)
配置文件中有以下两类宏。
(1)一类是选项(Options),前缀为“CONFIG_”,它们用于选择 CPU、SOC、开发板类型,设置系统时钟、选择设备驱动等。比如
#define CONFIG_ARM920T 1 /* This is an ARM920T Core */
#define CONFIG_S3C2410 1 /* in a SAMSUNG S3C2410 SoC */
#define CONFIG_SMDK2410 1 /* on a SAMSUNG SMDK2410 Board */
/* input clock of PLL */
#define CONFIG_SYS_CLK_FREQ 12000000 /* the SMDK2410 has 12MHz input clock */
//#define CONFIG_SYS_CLK_FREQ 16934400 /* the SMDK2410 has 12MHz input clock */
(2)另一类是参数(Setting),前缀为“CFG_”,它们用于设置 malloc 缓冲池的大小、U-Boot 的提示符、U-Boot 下载文件时的默认加载地址、Flash 的起始地址等。比如:
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024)
#define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */
从下面的编译、连接过程可知,U-Boot 中几乎每个文件都被编译和连接,但是这些文件是否包含有效的代码,则由宏开关来设置。比如对于网卡驱动 drivers/cs8900.c,它的格式为
如果定义了宏 CONFIG_DRIVER_CS8900,则文件中包含有效的代码;否则,文件被注释为空。
可以这样认为,“CONFIG_”除了设置一些参数外,主要用来设置 U-Boot 的功能、选择使用文件中的哪一部分;而“CFG_”用来设置更细节的参数。
编译,链接过程分析
配置完后,执行“make all”即可编译,从 Makefile 中可以了解 U-Boot 使用了哪些文件、哪个文件首先执行、可执行文件占用内存的情况。
先确定用到哪些文件,下面所示为 Makefile 中与 ARM 相关的部分
117 include $(OBJTREE)/include/config.mk
118 export ARCH CPU BOARD VENDOR SOC
119
…
127 ifeq ($(ARCH),arm)
128 CROSS COMPILE = arm-linux-
_
129 endif
…
163 # load other configuration
164 include $(TOPDIR)/config.mk
165
第 117、164 行用于包含其他的 config.mk 文件,第 117 行所要包含文件的就是在上面的配置过程中制作出来的 include/config.mk 文件,其中定义了 ARCH、CPU、BOARD、SOC 等4 个变量的值为 arm、arm920t、100ask24x0、s3c24x0。
第 164 行包含顶层目录的 config.mk 文件,它根据上面 4 个变量的值确定了编译器、编译选项等。其中对我们理解编译过程有帮助的是 BOARDDIR、LDFLAGS 的值,如下所示:
88 BOARDDIR = $(BOARD)
…
91 sinclude $(TOPDIR)/board/$(BOARDDIR)/config.mk # include board specific
rules
…
143 LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/U-Boot.lds
…
189 LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
在 board/100ask24x0/config.mk 中,定义了“TEXT_BASE = 0x33F80000”。所以,最终结果如下:BOARDDIR 为 100ask24x0;LDFLAGS 中有“-T board/100ask24x0/U-Boot.lds -Ttext 0x33F80000”字样。
继续往下看 Makefile:
166 #########################################################################167 # U-Boot objects....order is important (i.e. start must be first)
168
169 OBJS = cpu/$(CPU)/start.o
…
193 LIBS = lib_generic/libgeneric.a
194 LIBS += board/$(BOARDDIR)/lib$(BOARD).a
195 LIBS += cpu/$(CPU)/lib$(CPU).a
…
199 LIBS += lib_$(ARCH)/lib$(ARCH).a
200 LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/
libjffs2.a \
201 fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
202 LIBS += net/libnet.a
…
212 LIBS += $(BOARDLIBS)
213
……
从第 169 行得知,OBJS 的第一个值“cpu/$(CPU)/start.o”,即“cpu/arm920t/ start.o”。第 193~213 行指定了 LIBS 变量就是平台/开发板相关的各个目录、通用目录下相应的库,比如:lib_generic/libgeneric.a、board/smdk2410/libsmdk2410.a、cpu/arm920t/ libarm920t.a、lib_arm/libarm.a、fs/cramfs/libcramfs.a fs/fat/libfat.a 等。
OBJS、LIBS 所代表的.o、.a 文件就是 U-Boot 的构成,它们通过如下命令由相应的源文件(或相应子目录下的文件)编译得到。
268 $(OBJS):
269 $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))270
271 $(LIBS):
272 $(MAKE) -C $(dir $(subst $(obj),,$@))
273
274 $(SUBDIRS):
275 $(MAKE) -C $@ all
276
第 268、269 两行的规则表示,对于 OBJS 中的每个成员,都将进入 cpu/$(CPU)目录(即cpu/arm920t)编译它们。现在 OBJS 为 cpu/arm920t/start.o,它将由 cpu/arm920t/start.S 编译得到。
第 271、272 两行的规则表示,对于 LIBS 中的每个成员,都将进入相应的子目录执行“make”命令。这些子目录中的 Makefile,结构相似,它们将 Makefle 中指定的文件编译、连接成一个库文件。
当所有的 OBJS、LIBS 所表示的.o 和.a 文件都生成后,就剩最后的连接了,这对应 Makefile
中如下几行:
246 $(obj)U-Boot.srec: $(obj)U-Boot
247 $(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
248
249 $(obj)U-Boot.bin: $(obj)U-Boot
250 $(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
251
……
262 $(obj)U-Boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
263 UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u
_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
264 cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
265 --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
266 -Map U-Boot.map -o U-Boot
267
先使用第 262~266 的规则连接得到 ELF 格式的 U-Boot,最后转换为二进制格式U-Boot.bin、S-Record 格式 U-Boot.srec。LDFLAGS 确定了连接方式,其中的“-T board/100ask24x0/U-Boot.lds -Ttext 0x33F80000”字样指定了程序的布局、地址。board/100ask24x0/U-Boot.lds
文件如下:
28 SECTIONS
29 {
30 . = 0x00000000;
31
32 . = ALIGN(4);
33 .text :
34 {
35 cpu/arm920t/start.o (.text)
36 *(.text)
37 }
38
39 . = ALIGN(4);
40 .rodata : { *(.rodata) }
41
42 . = ALIGN(4);
43 .data : { *(.data) }
44
45 . = ALIGN(4);
46 .got : { *(.got) }
47
48 . = .;
49 u boot cmd start = .;
50 .u_boot_cmd : { *(.u_boot_cmd) }
51 u boot cmd end = .;
52
53 . = ALIGN(4);
54 bss start = .;
55 .bss : { *(.bss) }
56 end = .;
57 }
从第 35 行可知,cpu/arm920t/start.o 被放在程序的最前面,所以 U-Boot 的入口点在cpu/arm920t/start.S 中
现在来总结一下 U-Boot 的编译流程。
(1)首先编译 cpu/$(CPU)/start.S
,对于不同的 CPU,还可能编译 cpu/$(CPU)
下的其他文件。
(2)然后,对于平台/开发板相关的每个目录、每个通用目录都使用它们各自的 Makefile生成相应的库。
(3)将 1、2 步骤生成的.o、.a 文件按照 board/$(BOARDDIR)/config.mk
文件中指定的代码段起始地址、board/$(BOARDDIR)/U-Boot.lds
连接脚本进行连接。
(4)第 3 步得到的是 ELF 格式的 U-Boot,后面 Makefile 还会将它转换为二进制格式、S-Record 格式。