linux kernel makefile 编译过程概览
备注:使用make –debug=b 获得各个编译目标的依赖关系和顺序。
默认为 编译第一个目标 _all
make 后面没有指定目标,默认为 编译第一个目标 _all
以 -include 包含的文件,即使包含不到,也不会影响继续编译。比如
-include include/config/auto.conf
-include include/config/auto.conf.cmd
但是会尝试更新这个目标啊,
include/config/auto.conf.cmd 的规则是
.config include/config/auto.conf.cmd: ;
这个规则
target: prerequisite ; command
中prerequisite 和command 什么都没有,所以 include/config/auto.conf.cmd 目标就这样过了。
include/config/auto.conf 目标可以找到规则
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
即会按照这个规则来更新 include/config/auto.conf 目标 。
为了 include/config/auto.conf 文件,我们启动一个新的Make 执行
make -f ./Makefile silentoldconfig
第二轮进入Makefile,目标是silentoldconfig
,符合的rules
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
依赖是
Trying rule prerequisite 'scripts_basic'.
Trying rule prerequisite 'outputmakefile'.
Trying rule prerequisite 'FORCE'.
scripts_basic 目标,
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
outputmakefile
由于是在源码文件夹下面编译的,不需要拷贝Makefile 文件,相当于什么也没做。
三个依赖的目标更新后,开始更新目标
执行命令
make -f ./scripts/Makefile.build obj=scripts/kconfig silentoldconfig
scripts/kconfig/ 目录下面有 kbuild ? 或者 Makefile 里面找rules
依赖conf 文件,依赖文件完成后,
mkdir -p include/config include/generated
scripts/kconfig/conf --silentoldconfig Kconfig
这个命令的执行
需要包含 .config 文件,更新目标 include/linux/autoconf.h 文件,执行silentoldconfig
scripts/kconfig/conf --silentoldconfig Kconfig 完成后,
这步完成后,scripts/kconfig/Makefile 中的 silentoldconfig 目标完成;
Makefile 中的
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
目标完成
Makefile 中的
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
目标完成,即
include/config/auto.conf 文件完成
这个只是 由于 -include include/config/auto.conf 这句话引发的。
更新这个文件后,重新再Makefile 文件中包含 include/config/auto.conf 和 include/config/auto.conf.cmd 文件,
之后执行
make -d ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
1、编译开始后,首先做了什么?
没有目标,第一个 _all 目标作为要生成的目标
_all 依赖all,依赖vmlinux,依赖prepare ,依赖prepare2,依赖 include/config/auto.conf,所以什么参数都不带的 make 会来更新 include/config/auto.conf 这个目标
scripts 目标也依赖 include/config/auto.conf
这次,依然是include 这两个文件
-include include/config/auto.conf
-include include/config/auto.conf.cmd
都包含成功了
并且在 include/config/auto.conf.cmd 中,给目标 include/config/auto.conf 增加了新的依赖
$deps_config 就是各个目录下面的Kconfig 文件
======================================================
这次中,-include include/config/auto.conf时,又一次找到前面的规则
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
而且 include/config/auto.conf.cmd 中还给 include/config/auto.conf 增加了依赖文件
Considering target file 'include/config/auto.conf'.
Looking for an implicit rule for 'include/config/auto.conf'.
Trying pattern rule with stem 'auto'.
Trying rule prerequisite '.config'.
Trying rule prerequisite 'include/config/auto.conf.cmd'.
Found an implicit rule for 'include/config/auto.conf'.
Considering target file '.config'.
Finished prerequisites of target file '.config'.
No need to remake target '.config'.
Pruning file 'include/config/auto.conf.cmd'. -- 这句可以理解为 include/config/auto.conf.cmd 目标 刚更新过,不需要再次检查更新。
由于这些文件都比 'include/config/auto.conf'. 文件older , 所以 'include/config/auto.conf'. 文件已经是最新的了,不需要更新了。
Prerequisite 'Kconfig' is older than target 'include/config/auto.conf'.
No need to remake target 'include/config/auto.conf'.
之后更新
更新目标....
Considering target file '_all'.
File '_all' does not exist.
Considering target file 'all'.
File 'all' does not exist.
Considering target file 'vmlinux'.
File 'vmlinux' does not exist. vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
Considering target file 'scripts/link-vmlinux.sh'.
Looking for an implicit rule for 'scripts/link-vmlinux.sh'.
No implicit rule found for 'scripts/link-vmlinux.sh'.
Finished prerequisites of target file 'scripts/link-vmlinux.sh'.
No need to remake target 'scripts/link-vmlinux.sh'.
Considering target file 'arch/arm64/kernel/vmlinux.lds'.
File 'arch/arm64/kernel/vmlinux.lds' does not exist.
Considering target file 'init'. 'arch/arm64/kernel/vmlinux.lds' 依赖 init ,是规则$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
File 'init' does not exist.
Considering target file 'prepare'.
File 'prepare' does not exist.
Considering target file 'prepare0'.
File 'prepare0' does not exist.
Considering target file 'archprepare'.
File 'archprepare' does not exist.
Considering target file 'archheaders'.
File 'archheaders' does not exist.
Finished prerequisites of target file 'archheaders'.
Must remake target 'archheaders'.
Successfully remade target file 'archheaders'.
Considering target file 'archscripts'.
File 'archscripts' does not exist.
Finished prerequisites of target file 'archscripts'.
Must remake target 'archscripts'.
Successfully remade target file 'archscripts'.
Considering target file 'prepare1'.
File 'prepare1' does not exist.
Considering target file 'prepare2'.
File 'prepare2' does not exist.
Considering target file 'prepare3'.
File 'prepare3' does not exist.
Considering target file 'include/config/kernel.release'.
Pruning file 'include/config/auto.conf'.
Considering target file 'FORCE'.
File 'FORCE' does not exist.
Finished prerequisites of target file 'FORCE'.
Must remake target 'FORCE'.
Successfully remade target file 'FORCE'.
Finished prerequisites of target file 'include/config/kernel.release'.
Prerequisite 'include/config/auto.conf' is newer than target 'include/config/kernel.release'.
Prerequisite 'FORCE' of target 'include/config/kernel.release' does not exist.
Must remake target 'include/config/kernel.release'.
Putting child 0x24600f0 (include/config/kernel.release) PID 19083 on the chain.
Live child 0x24600f0 (include/config/kernel.release) PID 19083
CHK include/config/kernel.release
Reaping winning child 0x24600f0 PID 19083
Removing child 0x24600f0 PID 19083 from chain.
Successfully remade target file 'include/config/kernel.release'.
include/config/kernel.release 是第一个目标啊
使用了 filechk 函数,在kbuild.include 里面定义。更新 include/config/kernel.release 文件
执行的命令是
set -e;
echo ' CHK include/config/kernel.release';
mkdir -p include/config/;
echo "4.1.26$(/bin/bash ./scripts/setlocalversion .)" < include/config/auto.conf > include/config/kernel.release.tmp;
if [ -r include/config/kernel.release ] && cmp -s include/config/kernel.release include/config/kernel.release.tmp; then
rm -f include/config/kernel.release.tmp;
else
echo ' UPD include/config/kernel.release';
mv -f include/config/kernel.release.tmp include/config/kernel.release;
fi
文件里面的内容:
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)
所以正常情况prepare 3 什么也不做
Finished prerequisites of target file 'prepare3'.
Must remake target 'prepare3'.
Successfully remade target file 'prepare3'.
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.
asm-generic 第四个
# Support for using generic headers in asm-generic
PHONY += asm-generic
asm-generic:
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
src=asm obj=arch/$(SRCARCH)/include/generated/asm
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
src=uapi/asm obj=arch/$(SRCARCH)/include/generated/uapi/asm
./scripts/Makefile.asm-generic 文件的作用
# This Makefile reads the file arch/$(SRCARCH)/include/asm/Kbuild # and for each file listed in this file with generic-y creates # a small wrapper file in $(obj) (arch/$(SRCARCH)/include/generated/asm)
echo "#include " >arch/arm64/include/generated/asm/bug.h
。。。。。。
echo "#include " >arch/arm64/include/generated/uapi/asm/kvm_para.h
。。。。。。
asm_generic 完成后,prepare 2 的依赖就完成了,prepare 2 页什么没做,页完成。
Successfully remade target file 'prepare2'.
Considering target file 'include/generated/uapi/linux/version.h'.
接下来是prepare 1 的第二个依赖,
'include/generated/uapi/linux/version.h -- 第五个
Considering target file 'include/generated/uapi/linux/version.h'.
$(version_h): $(srctree)/Makefile FORCE
$(call filechk,version.h)
$(Q)rm -f $(old_version_h)
define filechk_version.h
(echo \#define LINUX_VERSION_CODE $(shell \
expr $(VERSION) \* 65536 + 0$(PATCHLEVEL) \* 256 + 0$(SUBLEVEL)); \
echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))';)
endef
那么 'include/generated/uapi/linux/version.h 里面是什么内容呢?
删除了old_version.h ,即include/linux/version.h
include/generated/utsrelease.h - 第六个
include/generated/utsrelease.h: include/config/kernel.release FORCE
$(call filechk,utsrelease.h)
uts_len := 64
define filechk_utsrelease.h
if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
exit 1; \
fi; \
(echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
endef
生成的 include/generated/utsrelease.h 是什么样子呢?
接下来是
prepare1 - 第七个
prepare1: prepare2 $(version_h) include/generated/utsrelease.h \
include/config/auto.conf
$(cmd_crmodverdir)
# Create temporary dir for module support files
# clean it up only when building all modules
cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) \
$(if $(KBUILD_MODULES),; rm -f $(MODVERDIR)/*)
mkdir -p .tmp_versions ; rm -f .tmp_versions/*
下来是 archprepare 依赖的 scripts_basic
scripts_basic 第八个
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
虽然scripts_basic 在之前已经做过了一次,但是由于它是 伪目标,不会有文件生成,所以现在还是要检查与生成一次的。
Successfully remade target file 'scripts_basic'.
Finished prerequisites of target file 'archprepare'.
Must remake target 'archprepare'.
Successfully remade target file 'archprepare'.
archprepare 的依赖,及其自己 就OK 啦
下来是 prepare 0 第九个
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
make -f ./scripts/Makefile.build obj=.
./scripts/Makefile.build 包含根目录下面的 Kbuild 文件来进行
# Kbuild for top-level directory of the kernel # This file takes care of the following: # 1) Generate bounds.h # 2) Generate asm-offsets.h (may need bounds.h) # 3) Check for missing system calls
include/generated/bounds.h
include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
prepare 0 结束,prepare 也结束
Finished prerequisites of target file 'prepare'.
Must remake target 'prepare'.
Successfully remade target file 'prepare'.
接下来是scripts
scripts: scripts_basic include/config/auto.conf include/config/tristate.conf \
asm-generic
$(Q)$(MAKE) $(build)=$(@)
又是一遍 scripts_basic, 不管了, include/config/auto.conf include/config/tristate.conf 都已经更新过了。
编译 scripts 文件夹下面的内容,依据scripts/Makefile 文件内容进行。
gcc -o scripts/dtc/dtc
gcc -o scripts/mod/modpost
scripts/kallsyms
scripts/conmakehash
scripts/sortextable
prepare 和 scripts 都更新之后,就是编译各个子目录了
规则是
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
Successfully remade target file 'scripts'.
Finished prerequisites of target file 'init'.
Must remake target 'init'.
vmlinux-dirs 为各个子目录名称,不带 / 符号。
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(net-y) $(net-m) $(libs-y) $(libs-m)))
make -f ./scripts/Makefile.build obj=init
编译的目录顺序为
init - init-y
usr - core-y in Makefile
arch/arm64/kernel in arm64/Makefile
arch/arm64/mm in arm64/Makefile
arch/arm64/net in arm64/Makefile
arch/arm64/crypto in arm64/Makefile
kernel
mm
fs
ipc
security
crypto
block
drivers
sound
firmware
net
arch/arm64/lib
lib
各个目录,及其子目录都编译完成后
Successfully remade target file 'lib'.
Finished prerequisites of target file 'arch/arm64/kernel/vmlinux.lds'.
执行到现在, 'arch/arm64/kernel/vmlinux.lds'. 已经是最新的了
Successfully remade target file 'arch/arm64/kernel/vmlinux.lds'.
Considering target file 'arch/arm64/kernel/head.o'.
继续vmlinux 的下一个依赖,
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
head-y 在 arch/arm64/Makefile文件中定义 head-y := arch/arm64/kernel/head.o
由于规则$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
所以arch/arm64/kernel/head.o 也是依赖所有的init usr 。。。。。。lib 这些目标,这些目标刚更新过,所以直接执行head.o 的command,head.o 也是最新的,所以不需要更新
一路检查下来,$(vmlinux-deps) 都不需要更新了
Finished prerequisites of target file 'vmlinux'.
Must remake target 'vmlinux'.
就更新vmlinux 了
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
/bin/bash scripts/link-vmlinux.sh aarch64-linux-gnu-ld -EL -p --no-undefined -X --build-id
脚本里面做了哪些事情:
,
. ./.config include .config
LD vmlinux.o 将上面的 中间文件链接为 vmlinux.o
modpost vmlinux.o to check for section mismatches
scripts/mod/modpost -o ./Module.symvers -S vmlinux.o
info GEN .version 生成 .version 文件
make -f ./scripts/Makefile.build obj=init 再生成一遍 init 文件夹
vmlinux_link '' .tmp_vmlinux1 # Link of vmlinux # ${1} - optional extra .o files # ${2} - output file
kallsyms .tmp_vmlinux1 .tmp_kallsyms1.o # Create ${2} .o file with all symbols from the ${1} object file
vmlinux_link .tmp_kallsyms1.o .tmp_vmlinux2 # Link of vmlinux # ${1} - optional extra .o files # ${2} - output file
kallsyms .tmp_vmlinux2 .tmp_kallsyms2.o # Create ${2} .o file with all symbols from the ${1} object file
vmlinux_link .tmp_kallsyms2.o vmlinux # Link of vmlinux # ${1} - optional extra .o files # ${2} - output file
sortextable vmlinux
mksysmap vmlinux System.map Create map file with all symbols from ${1}
# a) Verify that the System.map from vmlinux matches the map from # ${kallsymso}.
mksysmap .tmp_vmlinux2 .tmp_System.map
cmp -s System.map .tmp_System.map
rm -f .old_version
简化后,如下
LINK vmlinux
LD vmlinux.o
MODPOST vmlinux.o
GEN .version
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
KSYM .tmp_kallsyms1.o
KSYM .tmp_kallsyms2.o
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
Successfully remade target file '__build'.
KSYM .tmp_kallsyms1.o
KSYM .tmp_kallsyms2.o
LD vmlinux
SORTEX vmlinux
SYSMAP System.map
Successfully remade target file 'vmlinux'.
之后是 arch/arm64/Makefile 中给all 添加的其他依赖
'Image.gz'.
dtbs
之后是modules
all: modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
modules.builtin: $(vmlinux-dirs:%=%/modules.builtin)
$(Q)$(AWK) '!x[$$0]++' $^ > $(objtree)/modules.builtin
%/modules.builtin: include/config/auto.conf
$(Q)$(MAKE) $(modbuiltin)=$*
各个子目录下面的
make -f ./scripts/Makefile.modbuiltin obj=init
modules.builtin 目标是 生成 ./modules.builtin 文件
modules 目标是生成 ./modules.order
make -f ./scripts/Makefile.modpost
make -f ./scripts/Makefile.fwinst obj=firmware __fw_modbuild
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通