嵌入式Linux之uboot源码make编译逆向分析
目录
一、make编译逆向分析之prepare
在上一篇文章中分析Makefile文件依赖关系时采用自顶向下的方法,从顶层目标开始到最原始的依赖结束。
此处我们采用自下而上的方式,从最原始的依赖开始,一步一步的执行命令生成目标。
完整的prepare的目标依赖:
依次从最右边的依赖说起;
1.1 .config(配置阶段)
.config在执行make smdk2410_defconfig配置时生成,文件scripts/kconfig/Makefile中定义了所有匹配%_defconfig的目标:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
展开为:
smdk2410_defconfig: scripts/kconfig/conf
$(Q)scripts/kconfig/conf --defconfig=arch/../configs/smdk2410_defconfig Kconfig
在配置阶段,scripts/kconfig/conf搜集所有默认的Kconfig配置,然后用指定的smdk2410_defconfig配置进行更新并输出到根目录的.config文件中:
1.2 include/config/auto.conf的规则
在编译阶段,scripts/kconfig/conf读取配置阶段生成的.config,并检查最新配置生成以下4个文件:
- include/generated/autoconf.h;
- include/config/auto.conf.cmd;
- include/config/tristate.conf;
- include/config/auto.conf;
在生成include/config/auto.conf的规则中:
# 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 files include/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) 首先执行$(MAKE) -f $(srctree)/Makefile silentoldconfig外,即调用make -f ./Makefile silentoldconfig的最终结果是执行scripts/kconfig/Makefile中的规则:
silentoldconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated $< $(silent) --$@ $(Kconfig)
这个规则展开为:
silentoldconfig: scripts/kconfig/conf mkdir -p include/config include/generated scripts/kconfig/conf --silentoldconfig Kconfig
(2) 然后执行$(MAKE) -f $(srctree)/scripts/Makefile.autoconf,在scripts/Makefile.autoconf的文件头部定义有:
# This helper makefile is used for creating # - symbolic links (arch/$ARCH/include/asm/arch # - include/autoconf.mk, {spl,tpl}/include/autoconf.mk # - include/config.h # # When our migration to Kconfig is done # (= When we move all CONFIGs from header files to Kconfig) # this makefile can be deleted. # # SPDX-License-Identifier: GPL-2.0 # __all: include/autoconf.mk include/autoconf.mk.dep ifeq ($(shell grep -q '^CONFIG_SPL=y' include/config/auto.conf 2>/dev/null && echo y),y) __all: spl/include/autoconf.mk endif ifeq ($(shell grep -q '^CONFIG_TPL=y' include/config/auto.conf 2>/dev/null && echo y),y) __all: tpl/include/autoconf.mk endif
此处没有设置CONFIG_SPL=y或CONFIG_TPL=y,所以整个makefile的__all的依赖有:
- include/autoconf.mk
- include/autoconf.mk.dep
然而include/autoconf.mk还要进一步依赖于config.h。
1.2.1 include/config.h的规则
所有的autoconf.mk都依赖于include/config.h:
include/autoconf.mk include/autoconf.mk.dep \
spl/include/autoconf.mk tpl/include/autoconf.mk: include/config.h
实际上include/config.h由宏filechk_config_h生成:
# include/config.h # Prior to Kconfig, it was generated by mkconfig. Now it is created here. define filechk_config_h (echo "/* Automatically generated - do not edit */"; \ for i in $$(echo $(CONFIG_SYS_EXTRA_OPTIONS) | sed 's/,/ /g'); do \ echo \#define CONFIG_$$i \ | sed '/=/ {s/=/ /;q; } ; { s/$$/ 1/; }'; \ done; \ echo \#define CONFIG_BOARDDIR board/$(if $(VENDOR),$(VENDOR)/)$(BOARD);\ echo \#include \<config_defaults.h\>; \ echo \#include \<config_uncmd_spl.h\>; \ echo \#include \<configs/$(CONFIG_SYS_CONFIG_NAME).h\>; \ echo \#include \<asm/config.h\>; \ echo \#include \<config_fallbacks.h\>;) endef include/config.h: scripts/Makefile.autoconf create_symlink FORCE $(call filechk,config_h)
最终生成的include/config.h也比较简单,不妨看看:
/* Automatically generated - do not edit */ #define CONFIG_BOARDDIR board/samsung/smdk2410 #include <config_defaults.h> #include <config_uncmd_spl.h> #include <configs/smdk2410.h> #include <asm/config.h> #include <config_fallbacks.h>
生成config.h之前,还要应用create_symlink生成相应的符号链接。
1.2.2 create_symlink的规则
# symbolic links # If arch/$(ARCH)/mach-$(SOC)/include/mach exists, # make a symbolic link to that directory. # Otherwise, create a symbolic link to arch/$(ARCH)/include/asm/arch-$(SOC). PHONY += create_symlink create_symlink: ifdef CONFIG_CREATE_ARCH_SYMLINK ifneq ($(KBUILD_SRC),) $(Q)mkdir -p include/asm $(Q)if [ -d $(KBUILD_SRC)/arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \ dest=arch/$(ARCH)/mach-$(SOC)/include/mach; \ else \ dest=arch/$(ARCH)/include/asm/arch-$(if $(SOC),$(SOC),$(CPU)); \ fi; \ ln -fsn $(KBUILD_SRC)/$$dest include/asm/arch else $(Q)if [ -d arch/$(ARCH)/mach-$(SOC)/include/mach ]; then \ dest=../../mach-$(SOC)/include/mach; \ else \ dest=arch-$(if $(SOC),$(SOC),$(CPU)); \ fi; \ ln -fsn $$dest arch/$(ARCH)/include/asm/arch endif endif
注释已经很好解释了create_symlink的行为:
- 如果arch/$(ARCH)/mach-$(SOC)/include/mach存在,则生成符号链接:
arch/$(ARCH)/include/asm/arch --> arch/$(ARCH)/mach-$(SOC)
- 否则生成符号链接:
arch/$(ARCH)/include/asm/arch -->arch/$(ARCH)/include/asm/arch-$(SOC)
对基于arm v9架构的s3c24x0芯片,arch/arm/mach-s3c2440文件夹不存在,所以生成链接:
arch/arm/include/asm/arch --> arch/arm/include/asm/arch-s3c24x0
简单说来,create_symlink就是将芯片arch/arm/include/asm/arch-s3c24x0链接到跟芯片名字无关的arch/arm/include/asm下。
1.2.3 include/autoconf.mk的规则
# We are migrating from board headers to Kconfig little by little. # In the interim, we use both of # - include/config/auto.conf (generated by Kconfig) # - include/autoconf.mk (used in the U-Boot conventional configuration) # The following rule creates autoconf.mk # include/config/auto.conf is grepped in order to avoid duplication of the # same CONFIG macros quiet_cmd_autoconf = GEN $@ cmd_autoconf = \ $(CPP) $(c_flags) $2 -DDO_DEPS_ONLY -dM $(srctree)/include/common.h > $@.tmp && { \ sed -n -f $(srctree)/tools/scripts/define2mk.sed $@.tmp | \ while read line; do \ if [ -n "${KCONFIG_IGNORE_DUPLICATES}" ] || \ ! grep -q "$${line%=*}=" include/config/auto.conf; then \ echo "$$line"; \ fi \ done > $@; \ rm $@.tmp; \ } || { \ rm $@.tmp; false; \ } include/autoconf.mk: FORCE $(call cmd,autoconf)
从cmd_autoconf来看,这里会根据include/common.h的依赖,然后调用tools/scripts/define2mk.sed,并合并之前生成的include/config/auto.conf生成最终的autoconf.mk。
1.2.4 include/autoconf.mk.dep的规则
quiet_cmd_autoconf_dep = GEN $@ cmd_autoconf_dep = $(CC) -x c -DDO_DEPS_ONLY -M -MP $(c_flags) \ -MQ include/config/auto.conf $(srctree)/include/common.h > $@ || { \ rm $@; false; \ } include/autoconf.mk.dep: FORCE $(call cmd,autoconf_dep)
这个规则比较简单,由于autoconf.mk由common.h和auto.conf而来,因此直接处理这两个文件的依赖并合并到autoconf.mk.dep中。
1.3 include/config/uboot.release
define filechk_uboot.release echo "$(UBOOTVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))" endef # Store (new) UBOOTRELEASE string in include/config/uboot.release include/config/uboot.release: include/config/auto.conf FORCE $(call filechk,uboot.release)
命令$(call filechk,uboot.release)展开后就是调用宏filechk_uboot.release,最终将字符串2016.09写入include/config/uboot.release中。
1.4 timestamp.h和version.h的规则
ersion_h := include/generated/version_autogenerated.h timestamp_h := include/generated/timestamp_autogenerated.h ... # Generate some files # --------------------------------------------------------------------------- define filechk_version.h (echo \#define PLAIN_VERSION \"$(UBOOTRELEASE)\"; \ echo \#define U_BOOT_VERSION \"U-Boot \" PLAIN_VERSION; \ echo \#define CC_VERSION_STRING \"$$(LC_ALL=C $(CC) --version | head -n 1)\"; \ echo \#define LD_VERSION_STRING \"$$(LC_ALL=C $(LD) --version | head -n 1)\"; ) endef # The SOURCE_DATE_EPOCH mechanism requires a date that behaves like GNU date. # The BSD date on the other hand behaves different and would produce errors # with the misused '-d' switch. Respect that and search a working date with # well known pre- and suffixes for the GNU variant of date. define filechk_timestamp.h (if test -n "$${SOURCE_DATE_EPOCH}"; then \ SOURCE_DATE="@$${SOURCE_DATE_EPOCH}"; \ DATE=""; \ for date in gdate date.gnu date; do \ $${date} -u -d "$${SOURCE_DATE}" >/dev/null 2>&1 && DATE="$${date}"; \ done; \ if test -n "$${DATE}"; then \ LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_DATE "%b %d %C%y"'; \ LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_TIME "%T"'; \ LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_TZ "%z"'; \ LC_ALL=C $${DATE} -u -d "$${SOURCE_DATE}" +'#define U_BOOT_DMI_DATE "%m/%d/%Y"'; \ else \ return 42; \ fi; \ else \ LC_ALL=C date +'#define U_BOOT_DATE "%b %d %C%y"'; \ LC_ALL=C date +'#define U_BOOT_TIME "%T"'; \ LC_ALL=C date +'#define U_BOOT_TZ "%z"'; \ LC_ALL=C date +'#define U_BOOT_DMI_DATE "%m/%d/%Y"'; \ fi) endef $(version_h): include/config/uboot.release FORCE $(call filechk,version.h) $(timestamp_h): $(srctree)/Makefile FORCE $(call filechk,timestamp.h)
1.4.1 include/generated/version_autogenerated.h
根据include/config/uboot.release文件,规则调用filechk_version.h宏生成版本相关字符串文件include/generated/version_autogenerated.h,如下:
#define PLAIN_VERSION "2016.05" #define U_BOOT_VERSION "U-Boot " PLAIN_VERSION #define CC_VERSION_STRING "arm-none-linux-gnueabi-gcc (Sourcery G++ Lite 2008q3-72) 4.3.2" #define LD_VERSION_STRING "GNU ld (Sourcery G++ Lite 2008q3-72) 2.18.50.20080215
1.4.2 include/generated/timestamp_autogenerated.h
调用宏filechk_timestamp.h生成编译的时间戳文件,如下:
#define U_BOOT_DATE "Jun 07 2021" #define U_BOOT_TIME "01:58:59" #define U_BOOT_TZ "+0800" #define U_BOOT_DMI_DATE "06/07/2021"
1.5 outputmakfile的规则
PHONY += outputmakefile # outputmakefile generates a Makefile in the output directory, if using a # separate output directory. This allows convenient use of make in the # output directory. outputmakefile: ifneq ($(KBUILD_SRC),) $(Q)ln -fsn $(srctree) source $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) endif
- 如果编译没有设置O,即输出和代码都在同一个目录下,则outputmakefile的规则什么都不做;
- 如果编译指定了输出目录O,则调用scripts/mkmakefile在O选项指定的目录下生成一个简单的makefile
1.6 scripts_basic的规则
# Basic helpers built in scripts/ PHONY += scripts_basic scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount
scripts_basic的执行结果就是编译生成scripts/basic/fixdep工具,该工具是u-boot编译系统中最常用的工具,用于在编译过程中修正每一个生成文件的依赖关系。
1.7 parepare0的规则
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
展开后为:
prepare0: archprepare FORCE
make -f ./scripts/Makefile.build obj=.
编译时,命令make -f ./scripts/Makefile.build obj=.不会生成任何目标。
1.8 prepare系列目标总结
prepare阶段主要做了以下工作:
- scripts_basic规则生成fixdep工具,用于对整个系统生成目标文件相应依赖文件的更新;
- 配置阶段,scripts/kconfig/conf根据传入的指定配置文件在根目录下生成.config文件;
- 编译阶段,scripts/kconfig/conf读取配置阶段生成的.config,并检查最新配置生成以下文件:
- include/generated/autoconf.h
- include/config/auto.conf.cmd
- include/config/tristate.conf
- include/config/auto.conf
- 调用宏filechk_config_h生成include/config.h文件;
- 调用命令cmd_autoconf_dep生成autoconf.mk和autoconf.mk.cmd文件;
- 调用宏filechk_uboot.release生成include/config/uboot.release文件;
- 调用宏filechk_version.h生成include/generated/version_autogenerated.h文件;
- 调用宏filechk_timestamp.h生成include/generated/timestamp_autogenerated.h文件;
- 调用宏create_symlink就是将芯片arch/$(ARCH)/include/asm/arch-$(SOC)链接到跟芯片名字无关的arch/$(ARCH)/include/asm下。
二、make编译逆向分析之u-boot
完整的u-boot的目标依赖:
从图上可见,除了prepare依赖外,u-boot还依赖于文件$(head-y),$(libs-y)和$(LDSCRIPT),即依赖于:
- 启动文件arch/arm/cpu/$(CPU)/start.o ;
- 各个目录下的build-in.o ;
- 链接脚本文件arch/arm/cpu/u-boot.lds;
2.1 启动文件start.o
$(head-y)在arch/arm/Makefile中被直接指定:
head-y := arch/arm/cpu/$(CPU)/start.o
在顶层makefile中被指定给变量u-boot-init:
u-boot-init := $(head-y)
2.2 各目录下的build-in.o
$(libs-y)在顶层的makefile中被指定为各个子目录下的build-in.o的集合:
libs-y += lib/
...
libs-y += fs/
libs-y += net/
libs-y += disk/
libs-y += drivers/
...
libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)
libs-y := $(sort $(libs-y))
...
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
...
u-boot-main := $(libs-y)
以上脚本中,先将$(libs-y)设置为各子目录的集合,最后调用patsubst函数将$(libs-y)设置为这些目录下的built-in.o文件的集合,最后赋值给变量u-boot-main作为链接的主体文件。
各目录下的built-in.o是如何生成的呢?
以drivers/mmc/built-in.o为例,先查看生成的依赖文件drivers/mmc/.built-in.o.cmd:
cmd_drivers/mmc/built-in.o := rm -f drivers/mmc/built-in.o; arm-linux-ar rcs drivers/mmc/built-in.o
生成built-in.o的规则在scripts/Makefile.build中定义:
# # Rule to compile a set of .o files into one .o file # ifdef builtin-target quiet_cmd_link_o_target = LD $@ # If the list of objects to link is empty, just create an empty built-in.o cmd_link_o_target = $(if $(strip $(obj-y)),\ $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \ $(cmd_secanalysis),\ rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@) $(builtin-target): $(obj-y) FORCE $(call if_changed,link_o_target) targets += $(builtin-target) endif # builtin-target
2.3 链接脚本u-boot.lds
链接脚本的规则如下:
quiet_cmd_cpp_lds = LDS $@ cmd_cpp_lds = $(CPP) -Wp,-MD,$(depfile) $(cpp_flags) $(LDPPFLAGS) \ -D__ASSEMBLY__ -x assembler-with-cpp -P -o $@ $< u-boot.lds: $(LDSCRIPT) prepare FORCE $(call if_changed_dep,cpp_lds)
2.4 生成u-boot规则
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了