1、编译最终要生成的文件是$(obj)u-boot.bin文件:
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
$(obj)u-boot.bin是最终目标“all”的依赖文件,由make的规则可以知道,make会在当前文件中找目标为$(obj)u-boot.bin 的依赖性,再根据目标$(obj)u-boot.bin 的规则生成$(obj)u-boot.bin 文件。那我们就去找到$(obj)u-boot.bin 这个目标,如下:
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.bin这个目标又依赖于$(obj)u-boot,显然要想生成$(obj)u-boot.bin文件,就要找到 $(obj)u-boot这个目标。那我们先看一下生成$(obj)u-boot.bin文件的命令:
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@ ,我们将其展开,给到如下结果:
OBJCOPY = $(CROSS_COMPILE)objcopy
CROSS_COMPILE = arm-linux-
所以 OBJCOPY = arm-linux - objcopy
OBJCFLAGS += --gap-fill=0xff
$< = $(obj)u-boot ("$<"表示第一个依赖目标文件)
$@ = $(obj)u-boot.bin ("$@"表示目标集)
arm-linux - objcopy --gap-fill=0xff -O binary $(obj)u-boot $(obj)u-boot.bin ,想要知道这条命令的含义,那么就得知道arm-linux-objcopy这个命令的使用。从“objcopy”这个字眼大概可以知道这是一个目标文件(obj)的拷贝,从哪里拷贝到哪里呢?当然是从 “$(obj)u-boot” 拷贝到“$(obj)u-boot.bin ”因为我们这一步要得到的就是 “$(obj)u-boot.bin ”这个目标文件,这个文件的拷贝是以怎么的方式进行的呢?这就得看选项 “--gap-fill=0xff -O binary ”
gap-fill=0xff 指明了用oxff来填充section之间的空隙。
-O binary 在拷贝的时候进行格式转换,说明了“$(obj)u-boot.bin ”这个文件是一个binary(二进制)目标文件。
3、上面我们说了要想生成$(obj)u-boot这个依赖文件,就得找得$(obj)u-boot这个目标。
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
这个目标的命令是: $(GEN_UBOOT) ,再去找变量GEN_UBOOT的定义,如下:
GEN_UBOOT = \
UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
GEN_UBOOT 这个变量在“u-boot编译(二)”已经初步的分析过了,这里就直接引用过来,然后进一步深入分析:
先列举出这些变量,如下:
OBJDUMP =arm-linux-objdump
LIBBOARD = board/samsung/smdk2410/libsmdk2410.a
LIBS就是libs库(这里都是.a静态库),内容比较多就不列举出来了,详细请看“u-boot编译(二)”这一节
LNDIR := $(OBJTREE)
LD = arm-linux-ld
LDFLAGS += -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
__OBJS := $(subst $(obj),,$(OBJS))
__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
展开结果为:
GEN_UBOOT = \
UNDEF_SYM=`arm-linux-objdump -x board/samsung/smdk2410/libsmdk2410.a $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(OBJTREE) && arm-linux-ld -Bstatic -T $(obj)u-boot.lds $(PLATFORM_LDFLAGS)
$$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
我们也暂且不看 $(obj)u-boot的依赖文件,先来看一下, $(obj)u-boot这个目标是如何组织生成 $(obj)u-boot这个文件的。
UNDEF_SYM=`arm-linux-objdump -x board/samsung/smdk2410/libsmdk2410.a $(LIBS) | \
sed -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
就是使用“sed”命令修改库文件的内容,
$(obj)u-boot.lds 是一个链接脚本,用他来组织程序的存放位置,因此有必要来先分析这个文件。我们知道$(obj)u-boot.lds 是“$(obj)u-boot”这个目标的依赖文件,所以我们可以找到$(obj)u-boot.lds这个目标,看它是如何组织生成$(obj)u-boot.lds这个链接脚本文件。
$(obj)u-boot.lds文件分析
我们可以在Makefile找到这个目标:
$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
以上执行结果实质上是将cpu/arm920t/u-boot.lds经编译器简单预处理后输出到u-boot顶层目录下的u-boot文件。
$(obj)u-boot.lds这个目标又依赖于:$(LDSCRIPT)
$(LDSCRIPT): depend
$(MAKE) -C $(dir $@) $(notdir $@)
$(LDSCRIPT)又依赖于“depend”
depend dep: $(TIMESTAMP_FILE) $(VERSION_FILE) $(obj)include/autoconf.mk
for dir in $(SUBDIRS) cpu/$(CPU) $(dir $(LDSCRIPT)) ; do \
$(MAKE) -C $$dir _depend ; done
$(TIMESTAMP_FILE) $(VERSION_FILE)是时间戳和版本信息,我们重点不再这里,我们的重点是查看如何组织生成
$(obj)u-boot.lds这个脚本文件,
for dir in $(SUBDIRS) cpu/$(CPU) $(dir $(LDSCRIPT)) ; do \
$(MAKE) -C $$dir _depend ; done
这是个 for ... in .... do 语句
SUBDIRS = tools examples/standalone examples/api
CPU=arm920t
LDSCRIPT := $(TOPDIR)/lib_$(ARCH)/config.mk = $(TOPDIR)/lib_armconfig.mk
这里特别要注意“LDSCRIPT”这个变量:
一开始我就特么纠结,因为这个变量在在顶层的config.mk里有定义,LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds,以为要找的u-boot.lds文件在这个目录下,但是打开这个目录并未发现u-boot.lds这个文件,于是我就开始怀疑,在这之前可能定义过“LDSCRIPT”这个变量了,因为顶层的config.mk详细的定义是这样的:
ifndef LDSCRIPT
#LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds.debug
ifeq ($(CONFIG_NAND_U_BOOT),y)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-nand.lds
else
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds
endif
也就是在这之前没有定义过"LDSCPIPT",那么这里才定义。于是我用source insight这个软件去查找这个变量,发现在lib_arm/config.mk这个文件里有定义 "LDSCPIPT"这个变量 :LDSCRIPT := $(SRCTREE)/cpu/$(CPU)/u-boot.lds,那我就去找到哪个文件里包含了这个目录,通过顶层config.mk文件往上查找,可以找到:
ifdef ARCH
sinclude $(TOPDIR)/lib_$(ARCH)/config.mk # include architecture dependend rules
endif
这里的ARCH=arm,这不正是我们刚才找到的lib_arm/config.mk这个文件吗,所以LDSCRIPT 这个变量实际是在LDSCRIPT := $(SRCTREE)/cpu/$(CPU)/u-boot.lds 。因为老版本的u-boot与新版本的u-boot u-boot.lds 文件位置不一样,有些书写的针对的是老版本的(这里分析的是u-boot-2013.03),这里要特别注意,因为有可能你找到的 u-boot.lds 并不是你使用的 u-boot.lds 文件。
这里$(MAKE) 是make的递归调用,-C是执行后面目录下的makefile,那我们就列出上面的目录:
tools
examples/standalone
examples/api
cpu/arm920t
$(SRCTREE)/cpu/$(CPU)/u-boot.lds
_depend
就是执行这几个目录下的makefile
为了知道$(obj)u-boot.lds这个脚本文件是怎么生成的,我们就反回去看:
$(LDSCRIPT): depend
$(MAKE) -C $(dir $@) $(notdir $@)
命令展开结果为:$(MAKE) -C $(SRCTREE)/cpu/arm920t u-boot.lds
那我们就去cpu/arm920t
$(obj)u-boot.lds: $(LDSCRIPT)
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -ansi -D__ASSEMBLY__ -P - <$^ >$@
以上执行结果实质上是将cpu/arm920t/u-boot.lds经编译器简单预处理后输出到u-boot顶层目录下的u-boot文件。
默认变量“ CPP” :C 程序的预处理器(输出是标准输出设备)。默认命令是“$(CC) –E”。
-Dmacro:定义宏macro,宏的内容定义为字符串`1'. 这里
-D__ASSEMBLY__ 相当于 #define __ASSEMBLY__ ‘1’ 。-D 是选项,__ASSEMBLY__ 相当于一个宏。