前面第三章生成了.config,实际上还是不清楚细节上如何移植,不同的板子怎样设计.confg文件。
我们只是知道在.config里面 配置了很多CONFIG_XXX以及各种变量。
实际上先了解makefile构建uboot的整个流程。我们才能最终知道构建Uboot编译了哪些文件。
然后先编译一个rpi再编译几个其他板子,查看他们哪些编译是通用的。哪些编译是不通用的就知道移植一块板子到底需要修改哪些东西了。
执行make
首先遇到的第一目标就是
PHONY := _all
_all:
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
# If building an external module we do not care about the all: rule
# but instead _all depend on modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif
由于没有M命令。所以
_all:all
all: $(ALL-y)
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map binary_size_check
继续寻找依赖。发现 上面的 ALL-y最终都依赖u-boot。由u-boot产生的。
在真正执行make之前还要注意有几个-include
由于make没有带其他参数
所以
dot-config := 1
如果带了其他比如xxx_defconfig 就不会进入ifeq ($(dot-config,1)这里来
ifeq ($(dot-config),1)
-include include/config/auto.conf
-include include/config/auto.conf.cmd
-include include/autoconf.mk
-include include/autoconf.mk.dep
include config.mk
include arch/$(ARCH)/Makefile
endif
这几个文件分别生成了 很多其他的文件
初始的时候auto.conf auto.conf.cmd autoconf.mk autoconf.mk.dep都是不存在的。所以会首先使用隐含的规则查找构建这个目标。构建完毕后再执行一次make
和原来的比较一下就知道这几个Include分别产生了什么文件
1 顶层目录多了u-boot.cfg
2 include目录多了config.h autoconf.mk autoconf.mk.dep
3 include目录多了 generated目录以及目录里面的autoconf.h
4 include目录多了一个config目录。以及目录里面各种头文件(大部分是空文件,只有一个文件名,0字节)(应该是为下一版本准备或者之前移植过来尚未处理的)
基本上都是一些基础配置数据。主要的一些配置数据在config/auto.conf里面。比如CONFIG_CPU_ARM1176=y CONFIG_SYS_VENDOR="raspberrypi"等等。
后续include config.mk里面需要这些变量。
等头文件什么都包含了再回头再看u-boot是如何产生的
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
先分别看三个依赖
1$(u-boot-init)
u-boot-init := $(head-y)
head-y由于包含了include arch/$(ARCH)/Makefile。
里面有head-y := arch/arm/cpu/$(CPU)/start.o
所以 u-boot-init就是start.o这个
2$(u-boot-main)
u-boot-main := $(libs-y)
也就是各种库和各种驱动
3u-boot.lds有两个依赖
u-boot.lds: $(LDSCRIPT) prepare FORCE
$(call if_changed_dep,cpp_lds)
一个是LDSCRIPT
如果没有定义LDSCRIPT,那么去下面这三个地方寻找
ifndef LDSCRIPT
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds
endif
endif
还有一个依赖是prepare
prepare: prepare0
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
archprepare: prepare1 scripts_basic
scripts_basic看前面make xxx_defconfig的分析
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
prepare1: prepare2 $(version_h) $(timestamp_h) \
include/config/auto.conf
prepare1产生了$(version_h) $(timestamp_h)
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
构建过程如下
$(version_h): include/config/uboot.release FORCE
$(call filechk,version.h)
$(timestamp_h): $(srctree)/Makefile FORCE
$(call filechk,timestamp.h)
prepare2: prepare3 outputmakefile
其中outputmakefile一半得不到执行。主要是outputmakefile用于设置了独立的编译目录情况下才使用到
prepare3: include/config/uboot.release
prepare3和前面的$(version_h)都依赖于 include/config/uboot.release
include/config/uboot.release: include/config/auto.conf FORCE
$(call filechk,uboot.release)
而include/config/uboot.release和prepare1也依赖于include/config/auto.conf
因此命中目标
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
$(Q)touch include/config/auto.conf
这里KCONFIG_CONFIG=.config include/config/auto.conf.cmd 被强制更新
但是是个空命令
$(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
接下来就是执行
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
其中
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig 命中目标
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
这里和配置里面的make xxx流程就一样了。不同的是参数是silentoldconfig 而不是rpi_defconfig了
这个silentoldconfig主要是创建了include/config目录以及里面的各种conf文件。关键的是 auto.conf.cmd、autoconf.h、tristate.conf、auto.conf
等文件
接下来执行
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf
也就是产生__all: include/autoconf.mk include/autoconf.mk.dep
其中依次创建了include/config.h 还创建了一些符号链接。主要是将arch/arm/include/asm/arch连接到对应的板子目录当中去
最终执行了最后的
$(call if_changed,u-boot__) 链接过程产生u-boot
cmd_u-boot__ ?= $(LD) $(LDFLAGS) $(LDFLAGS_u-boot) -o $@ \
-T u-boot.lds $(u-boot-init) \
--start-group $(u-boot-main) --end-group \
$(PLATFORM_LIBS) -Map u-boot.map
完全展开得到
arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x00008000 -o u-boot -T u-boot.lds arch/arm/cpu/arm1176/start.o --start-group arch/arm/cpu/built-in.o arch/arm/cpu/arm1176/built-in.o arch/arm/lib/built-in.o arch/arm/mach-bcm283x/built-in.o board/raspberrypi/rpi/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/mmc/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/domain/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.o --end-group arch/arm/lib/eabi_compat.o arch/arm/lib/lib.a -Map u-boot.map
查看一下使用的的编译选项
-pie 创建一个位置独立的可执行文件
这一点参看http://blog.chinaunix.net/uid-20528014-id-4445271.html
http://hardenedlinux.org/system-security/2016/07/01/PIC_and_PIE_analysis.html
因为uboot存放的位置一般是ROM,无法写,所以一般要把UBOOT移动到 RAM里面再运行。
如果不是位置无关的代码。拷贝的时候对于全局变量的操作都需要修改代码。如果是位置无关的代码。只需要修改后面的表里面的数据。
--gc-sections 删除掉没有用到的代码段。减小体积。
-o u-boot 输出文件叫做u-boot
-Bstatic 指定使用静态方式连接 比如后面的lib.a uboot肯定要静态链接,他不能还去依赖其他的库。
-Ttext 0x00008000设置.text段的地址
-T uboot.lds 使用lds连接脚本来进行连接
--start-group 和--end-group主要是解决之间的文档的依赖问题。通常一般只搜索一次,按照命令行的顺序,比如 -liba -libb 如果a需要b的符号,但是b排在了a的后面,这时候就会报错,需要把b放到a的前面才行。但是加上--start-group和--end-group以后就会重复搜索,直到找到这个依赖。
-Map u-boot.map 生成一个map文件
这里只简单讲了基本的整个构建过程,Obj文件这些后续再看。