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

image

依赖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 增加了新的依赖

image

$deps_config 就是各个目录下面的Kconfig 文件

image

======================================================

这次中,-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

文件里面的内容:

image

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 里面是什么内容呢?

image

删除了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 是什么样子呢?

image

接下来是

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

posted @ 2022-02-12 13:23  张志伟122  阅读(1147)  评论(0编辑  收藏  举报