1. 程式人生 > 其它 >linux kernel makefile 編譯過程概覽

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