u-boot-2016.09 make编译过程分析(一)
来源:互联网 发布:linux从入门到精通pdf 编辑:程序博客网 时间:2024/06/06 01:47
u-boot-2016.09 make编译过程分析(一)
综述
u-boot
自v2014.10
版本开始引入KBuild
系统,Makefile的管理和组织跟以前版本的代码有了很大的不同,其Makefile更加复杂。整个Makefile中,嵌套了很多其它不同用途的Makefile,各种目标和依赖也很多,make分析很容易陷进去,让人摸不着头脑。
u-boot的编译跟kernel编译一样,分两步执行:
- 第一步:配置,执行make xxx_defconfig
进行各项配置,生成.config
文件
- 第二部:编译,执行make
进行编译,生成可执行的二进制文件u-boot.bin或u-boot.elf
上一篇博客《u-boot-2016.09 make配置过程分析》详尽解释了第一步的操作,在这一步中,u-boot执行配置命令make xxx_defconfig
时先搜集所有默认的Kconfig
配置,然后再用命令行指定的xxx_defconfig
配置进行更新并输出到根目录的.config
文件中。
本文着眼第二步,即配置完成后执行make命令生成二进制文件的过程,由于涉及的依赖和命令很多,也将make编译过程分析分为两部分,目标依赖和命令执行。
Makefile的核心是依赖和命令。对于每个目标,首先会检查依赖,如果依赖存在,则执行命令更新目标;如果依赖不存在,则会以依赖为目标,先生成依赖,待依赖生成后,再执行命令生成目标。
第一部分、目标依赖
现在来分析u-boot编译执行make命令的依赖关系。
目标依赖分析采用自顶向下方式,从顶层目标开始,逐次向下分解每一层依赖,直到不能分解位置。
1. 顶层目标依赖
a). _all
和all
对$(ALL-y)
的依赖
从顶层Makefile开始查找,首先找到的是_all
伪目标:
# That's our default target when none is given on the command linePHONY := _all_all:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
紧接着会对_all
伪目标添加all
伪目标的依赖:
# If building an external module we do not care about the all: rule# but instead _all depend on modulesPHONY += allifeq ($(KBUILD_EXTMOD),)_all: allelse_all: modulesendif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
all
自身依赖于$(ALL-y)
all: $(ALL-y)
- 1
- 1
b).$(ALL-y)
对u-boot
目标文件的依赖
$(ALL-y)
定义了最终需要生成所有文件:
# Always append ALL so that arch config.mk's can add custom onesALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_checkALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.binifeq ($(CONFIG_SPL_FSL_PBL),y)ALL-$(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.binelseifneq ($(CONFIG_SECURE_BOOT), y)# For Secure Boot The Image needs to be signed and Header must also# be included. So The image has to be built explicitlyALL-$(CONFIG_RAMBOOT_PBL) += u-boot.pblendifendifALL-$(CONFIG_SPL) += spl/u-boot-spl.binALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.imgALL-$(CONFIG_TPL) += tpl/u-boot-tpl.binALL-$(CONFIG_OF_SEPARATE) += u-boot.dtbifeq ($(CONFIG_SPL_FRAMEWORK),y)ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb.imgendifALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtbifneq ($(CONFIG_SPL_TARGET),)ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%)endifALL-$(CONFIG_REMAKE_ELF) += u-boot.elfALL-$(CONFIG_EFI_APP) += u-boot-app.efiALL-$(CONFIG_EFI_STUB) += u-boot-payload.efiifneq ($(BUILD_ROM),)ALL-$(CONFIG_X86_RESET_VECTOR) += u-boot.romendif# enable combined SPL/u-boot/dtb rules for tegraifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.binALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb-tegra.binendif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
以上的$(ALL-y)
目标中看起来非常复杂,但除了第一行的通用目标外,其余目标都只在特殊条件下才生成,这里略去不提。只分析通用目标依赖:
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
- 1
- 1
i. 依赖u-boot.srec
依赖u-boot.srec
:
u-boot.hex u-boot.srec: u-boot FORCE $(call if_changed,objcopy)
- 1
- 2
- 3
- 1
- 2
- 3
ii. 依赖u-boot.bin
依赖u-boot.bin
:
ifeq ($(CONFIG_OF_SEPARATE),y)u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE $(call if_changed,cat)u-boot.bin: u-boot-dtb.bin FORCE $(call if_changed,copy)elseu-boot.bin: u-boot-nodtb.bin FORCE $(call if_changed,copy)endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
如果打开了device tree支持,则有依赖关系: u-boot.bin --> u-boot-dtb.bin --> u-boot-nodtb.bin + dts/dt.dtb
这里没有打开device tree支持,所以: u-boot.bin --> u-boot-nodtb.bin
进一步,对于u-boot-nodtb.bin
,其规则是:
u-boot-nodtb.bin: u-boot FORCE $(call if_changed,objcopy) $(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE)) $(BOARD_SIZE_CHECK)
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
iii. 依赖u-boot.sym
依赖u-boot.sym
:
u-boot.sym: u-boot FORCE $(call if_changed,sym)
- 1
- 2
- 1
- 2
iv. 依赖System.map
依赖System.map
:
System.map: u-boot @$(call SYSTEM_MAP,$<) > $@
- 1
- 2
- 1
- 2
v. 依赖u-boot.cfg
依赖u-boot.cfg
:
u-boot.cfg: include/config.h FORCE $(call if_changed,cpp_cfg)
- 1
- 2
- 1
- 2
vi. 依赖binary_size_check
依赖binary_size_check
:
binary_size_check: u-boot-nodtb.bin FORCE @file_size=$(shell wc -c u-boot-nodtb.bin | awk '{print $$1}') ; \ map_size=$(shell cat u-boot.map | \ awk '/_image_copy_start/ {start = $$1} /_image_binary_end/ {end = $$1} END {if (start != "" && end != "") print "ibase=16; " toupper(end) " - " toupper(start)}' \| sed 's/0X//g' \| bc); \if [ "" != "$$map_size" ]; then \ if test $$map_size -ne $$file_size; then \ echo "u-boot.map shows a binary size of $$map_size" >&2 ; \echo " but u-boot-nodtb.bin shows $$file_size" >&2 ; \ exit 1; \ fi \ fi
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
显然对于binary_size_check
有下列依赖关系: binary_size_check --> u-boot-nodtb.bin --> u-boot
vii.$(ALL-y)
依赖目标的共同点
以上通用目标$(ALL-y)
的依赖有一个共同点,除了u-boot.cfg
依赖于include/config.h
外,其余目标全都依赖于u-boot
(实际上除了依赖于u-boot
外,还依赖于FORCE
,由于FORCE
依赖本身是一个空目标,为了方便,这里略去了对FORCE
依赖的描述),如下:
2. u-boot
文件目标依赖
a). 依赖u-boot
依赖u-boot
:
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE $(call if_changed,u-boot__)ifeq ($(CONFIG_KALLSYMS),y) $(call cmd,smap) $(call cmd,u-boot__) common/system_map.oendif
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
其中$(u-boot-init)
和$(u-boot-main)
分别被定义为:
u-boot-init := $(head-y)u-boot-main := $(libs-y)
- 1
- 2
- 1
- 2
i. 依赖$(head-y)
$(head-y)
在arch/arm/Makefile
被定义为:
head-y := arch/arm/cpu/$(CPU)/start.o
- 1
- 1
ii. 依赖$(libs-y)
在顶层Makefile中搜索一下$(libs-y)
,其被定义为各层驱动目录下build-in.o
的集合:
ygu@ubuntu:/opt/work/u-boot/u-boot-2016.09$ grep -nw libs-y Makefile629:libs-y += lib/632:libs-y += fs/633:libs-y += net/634:libs-y += disk/635:libs-y += drivers/636:libs-y += drivers/dma/637:libs-y += drivers/gpio/638:libs-y += drivers/i2c/639:libs-y += drivers/mmc/640:libs-y += drivers/mtd/642:libs-y += drivers/mtd/onenand/644:libs-y += drivers/mtd/spi/645:libs-y += drivers/net/646:libs-y += drivers/net/phy/647:libs-y += drivers/pci/648:libs-y += drivers/power/ \655:libs-y += drivers/spi/659:libs-y += drivers/serial/660:libs-y += drivers/usb/dwc3/661:libs-y += drivers/usb/common/662:libs-y += drivers/usb/emul/663:libs-y += drivers/usb/eth/664:libs-y += drivers/usb/gadget/665:libs-y += drivers/usb/gadget/udc/666:libs-y += drivers/usb/host/667:libs-y += drivers/usb/musb/668:libs-y += drivers/usb/musb-new/669:libs-y += drivers/usb/phy/670:libs-y += drivers/usb/ulpi/671:libs-y += cmd/672:libs-y += common/675:libs-y += test/676:libs-y += test/dm/680:libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)682:libs-y := $(sort $(libs-y))684:u-boot-dirs := $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples688:libs-y := $(patsubst %/, %/built-in.o, $(libs-y))691:u-boot-main := $(libs-y)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
第一行的搜索命令grep -nw libs-y Makefile
参数:
- -n
表示搜索结果显示行号
- -w
表示仅搜索完成的单词
- Makefile
表示仅在当前目录的文件Makefile
中搜索
以上搜索结果,从629~682的各个匹配行都是将驱动的各个目录包含进来,第688行上会在每个目录名称的后面添加build-in.o
,例如libs-y
中的mtd驱动目录drivers/mtd/
会变成drivers/mtd/build-in.o
,这样就仅相当于链接每个驱动目录下的build-in.o
文件。
为什么只是每个目录下的build-in.o
文件呢?答案是编译时将同一个目录下的多个*.o
输出文件合并到一起生成一个build-in.o
文件,后面会有另外的博客对此专门说明。
b). 依赖u-boot.lds
依赖u-boot.lds
:
u-boot.lds: $(LDSCRIPT) prepare FORCE $(call if_changed_dep,cpp_lds)
- 1
- 2
- 1
- 2
其中$(LDSCRIPT)
的定义如下:
# If board code explicitly specified LDSCRIPT or CONFIG_SYS_LDSCRIPT, use# that (or fail if absent). Otherwise, search for a linker script in a# standard location.ifndef LDSCRIPT #LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds.debug ifdef CONFIG_SYS_LDSCRIPT # need to strip off double quotes LDSCRIPT := $(srctree)/$(CONFIG_SYS_LDSCRIPT:"%"=%) endifendif# If there is no specified link script, we look in a number of places for itifndef 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 endifendif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
如果没有定义LDSCRIPT
和CONFIG_SYS_LDSCRIPT
,则默认使用u-boot自带的lds文件。包括board/$(BOARDIDR)
和$(CPUDIR)
目录下定制的针对board或cpu的lds文件,如果没有定制的lds文件,则采用arch/$(ARCH)/cpu
目录下默认的u-boot.lds
我们分析针对树莓派3代平台,其配置rpi_3_32b_defconfig
没有对应任何特定的lds文件,所以使用默认文件arch/arm/cpu/u-boot.lds
依赖prepare
u-boot.lds
的另一个依赖就是伪目标prepare
。
u-boot
文件目标依赖
u-boot文件目标的依赖总体起来就是这样:
3. prepare
系列目标依赖
a). prepare
系列依赖的规则
实际上prepare
是一系列prepare
伪目标和动作的组合,完成编译前的准备工作:
# Things we need to do before we recursively start building the kernel# or the modules are listed in "prepare".# A multi level approach is used. prepareN is processed before prepareN-1.# archprepare is used in arch Makefiles and when processed asm symlink,# version.h and scripts_basic is processed / created.# Listed in dependency orderPHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3# prepare3 is used to check if we are building in a separate output directory,# and if so do:# 1) Check that make has not been executed in the kernel src $(srctree)prepare3: include/config/uboot.releaseifneq ($(KBUILD_SRC),) @$(kecho) ' Using $(srctree) as source for U-Boot' $(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then \ echo >&2 " $(srctree) is not clean, please run 'make mrproper'"; \ echo >&2 " in the '$(srctree)' directory.";\ /bin/false; \ fi;endif# prepare2 creates a makefile if using a separate output directoryprepare2: prepare3 outputmakefileprepare1: prepare2 $(version_h) $(timestamp_h) \ include/config/auto.confifeq ($(wildcard $(LDSCRIPT)),) @echo >&2 " Could not find linker script." @/bin/falseendifarchprepare: prepare1 scripts_basicprepare0: archprepare FORCE $(Q)$(MAKE) $(build)=.# All the preparing..prepare: prepare0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
伪目标prepare
,prepare0
,archprepare
,prepare1
,prepare2
,prepare3
之间的依赖如下:
b).prepare
系列其他的依赖规则
在prepare1
的依赖列表中,除了include/config/auto.conf
外,还有$(version_h)
和$(timestamp_h)
,他们的依赖分别为:
$(version_h): include/config/uboot.release FORCE $(call filechk,version.h)$(timestamp_h): $(srctree)/Makefile FORCE $(call filechk,timestamp.h)
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
这里的两个filechk
函数调用会动态生成version.h
和timestamp.h
。
对于最里层的prepare3
的依赖include/config/uboot.release
,还存在下一级依赖:
# Store (new) UBOOTRELEASE string in include/config/uboot.releaseinclude/config/uboot.release: include/config/auto.conf FORCE $(call filechk,uboot.release)
- 1
- 2
- 3
- 1
- 2
- 3
对于include/config/auto.conf
,Makefile中有一个匹配的规则:
# If .config is newer than include/config/auto.conf, someone tinkered# with it and forgot to run make oldconfig.# if auto.conf.cmd is missing then we are probably in a cleaned tree so# we execute the config step to be sure to catch updated Kconfig filesinclude/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig @# If the following part fails, include/config/auto.conf should be @# deleted so "make silentoldconfig" will be re-run on the next build. $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \ { rm -f include/config/auto.conf; false; } @# include/config.h has been updated after "make silentoldconfig". @# We need to touch include/config/auto.conf so it gets newer @# than include/config.h. @# Otherwise, 'make silentoldconfig' would be invoked twice. $(Q)touch include/config/auto.conf
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
所以include/config/auto.conf
依赖于$(KCONFIG_CONFIG)
和include/config/auto.conf.cmd
。
- $(KCONFIG_CONFIG)
实际上就是.config
文件;
- include/config/auto.conf.cmd
是由fixdep
在编译时生成的依赖文件;
c).prepare
系列伪目标完整的依赖关系
整个prepare
部分的依赖关系如下:
4. 完整的目标依赖
将上面的依赖关系并到一起,就得到了一个完整的u-boot
目标依赖图:
(完整的关系图较大,可以将图片拖到浏览器的其他窗口看大图)
完成目标依赖分析后,剩下的就是基于完整的目标依赖关系图,从最底层的依赖开始,逐层运行命令生成目标,直到生成顶层目标。
- u-boot-2016.09 make编译过程分析(一)
- u-boot-2016.09 make编译过程分析(一)
- 【u-boot】u-boot-2016.09 make编译过程分析(一)
- u-boot-2016.09 make编译过程分析(二)
- u-boot-2016.09 make编译过程分析(二)
- 【u-boot】u-boot-2016.09 make编译过程分析(二)
- u-boot-2016.09 make配置过程分析
- u-boot-2016.09 make配置过程分析
- 【u-boot】u-boot-2016.09 make配置过程分析
- 【u-boot】u-boot-2017.05启动过程分析(一)
- U-Boot编译过程分析
- u-boot编译过程分析
- u-boot编译过程分析
- u-boot编译过程分析
- u-boot编译过程一
- u-boot启动过程分析(一)
- U-Boot编译过程完全分析2-include/autoconf.mk和make all
- U-Boot编译过程完全分析2-include/autoconf.mk和make all
- [android-wifi]802.11-管理和控制和数据帧格式
- 《Qt 实战一二三》
- centos7常见问题总结(一)
- PHP从环境搭建到编写简单接口
- location.href 在JS中是 什么意思 怎么使用它 举例说明谢谢了,大神帮忙啊
- u-boot-2016.09 make编译过程分析(一)
- [leetcode] 8. String to Integer (atoi)
- qt-faststart设关键帧-last atom in file was not a moov atom
- LeetCode 26. Remove Duplicates from Sorted Array
- Android初级开发(四)——补充7、Gallery(画廊)的使用
- 求反射向量
- HDU 6092 Rikka with Subset
- Textbox禁止剪切,复制,粘贴和弹出右键菜单
- Servlet应用程序(3)----验证器