linux源碼Makefile的詳細分析

目錄linux

1、概述編程

  一、本文的意義架構

  二、Linux內核Makefile文件組成less

2、Linux內核Makefile的「make解析」過程ide

  1 頂層Makefile階段函數

    一、從總目標uImage提及 oop

    二、vmlinux的生成post

    三、vmlinux-lds、vmlinux-init、vmlinux-main的生成ui

  2 scripts/Makefile.build的第一次調用階段spa

    一、Makefile.build的包含文件

    二、scripts/Makefile.build的總目標

    三、drivers/built-in.o的生成

    四、drivers/net/built-in.o

  3 scripts/Makefile.build的再一次調用階段

    一、Makefile.build的包含文件

    二、scripts/Makefile.build的總目標

    三、drivers/net/built-in.o的生成

    四、drivers/net/dm9000.o

    五、drivers/net/e1000/built-in.o的生成

3、Linux內核總體編譯過程

4、Linux內核編譯構成元素

  一、Makefile的目標

  二、Makefile的依賴

  三、Makefile的規則

  四、Makefile的編譯鏈接選項

5、Linux內核Makefile特色 

  一、兩個Makefile

  二、編譯的目錄始終是頂層目錄

  三、通用規則

 

1、概述

一、本文的意義

  衆多的資料(《嵌入式Linux應用開發徹底手冊》、Documentation/kbuild/makefiles.txt)已經向咱們展現了一個初級Linux用戶者應該懂得的知識--怎樣添加須要編譯的文件、添加編譯的規則、多個源文件構成一個目標文件的狀況等。

  可是,一種「找到真相」的衝動迫使我想了解Linux內核編譯的整個過程是怎樣的。爲此,查了不少資料,發現《深度探索Linux操做系統:系統構建和原理解析》一文的第三章對該問題有很詳細的論述。

  通過一番分析,也有本身的想法,寫於此。本文主要討論的是Linux內核編譯的整個過程,以此爲基礎,再來討論諸如Linux內核Makefile的特色、構成元素等其餘的問題。

  對於分析Linux Makefile的整個編譯過程,筆者認爲須要對Makefile有必定的基礎。若是以前沒接觸過Makefile的,不建議看本文。另外,對於那些只須要初級認識的Linux用戶也無需對此付出過多精力,能夠直接飄過。

二、Linux內核Makefile文件組成

Linux內核Makefile文件組成
名稱 描述
頂層 Makefile 它是全部Makefile文件的核心,從整體上控制着內核的編譯、鏈接
arch/$(ARCH)/Makefile 對應體系結構的Makefile,它用來決定哪些體系結構相關的文件參與內核的生成,並提供一些規則來生成特定格式的內核映像
scripts/Makefile.* Makefile公用的通用規則、腳本等
子目錄kbuild Makefiles 各級子目錄的Makefile相對簡單,被上一層Makefile.build調用來編譯當前目錄的文件。
頂層.config 配置文件,配置內核時生成。全部的Makefile文件(包括頂層目錄和各級子目錄)都是根據.config來決定使用哪些文件的

 scripts/Makefile.*中有公用的通用規則,展現以下:

scripts/Makefile.*文件組成
Makefile.build 被頂層Makefile所調用,與各級子目錄的Makefile合起來構成一個完整的Makefile文件,定義.lib、built-in.o以及目標文件.o的生成規則。這個Makefile文件生成了子目錄的.lib、built-in.o以及目標文件.o
Makefile.clean 被頂層Makefile所調用,用來刪除目標文件等
Makefile.lib 被Makefile.build所調用,主要是對一些變量的處理,好比說在obj-y前邊加上obj目錄
Kbuild.include 被Makefile.build所調用,定義了一些函數,如if_changed、if_changed_rule、echo-cmd

總結:

  Linux內核Makefile體系核心的Makefile文件就兩個:頂層Makefile、scripts/Makefile.build。

  子目錄中的Makefile、kbuild不是Makefile文件(完整的Makefile文件),只能算做是Makefile的包含文件。

  頂層Makefile文件負責將各個目錄生成的*.built-in.o、lib.a等文件鏈接到一塊兒。而scripts/Makefile.build 包含子目錄中的Makefile文件來生成這些*.built-in.o、lib.a、*.o等文件。

2、Linux內核Makefile的「make解析」過程

  本文的實驗源碼是對「linux-2.6.30.4」進行移植後的運行在TQ2440開發板上的源碼包。

1 頂層Makefile階段

一、從總目標uImage提及

  內核配置完成後,在頂層目錄中執行「#make uImage」便開始編譯內核。可是,uImage卻不是在頂層Makefile中定義,而是在arch/$(ARCH)/Makefile中定義。

頂層Makefile Line 452: 
include $(srctree)/arch/$(SRCARCH)/Makefile

  其中srctree爲源碼絕對路徑,以個人環境爲例,它的值等於/workspace/linux-2.6.30.4;而SRCARCH := $(ARCH),即該變量等於架構名稱,咱們以arm爲例進行說明。 

arch/arm/Makefile Line 230: 
zImage Image xipImage bootpImage uImage: vmlinux

  可見uImage依賴於vmlinux,要先生成vmlinux,而後執行下邊這條指令完成編譯。

arch/arm/Makefile Line 231: 
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

<1> Q的定義:選擇靜態編譯與否(是否打印編譯信息)

頂層Makefile Line 290:
ifeq ($(KBUILD_VERBOSE),1) quiet = Q = else quiet=quiet_ Q = @ endif

<2> MAKE: 系統環境變量,值爲make

<3> build: 值爲「-f scripts/Makefile.build obj=」實際上就是調用子Makefile--scripts/Makefile.build,而後傳遞參數目標文件夾。

Kbuild.include Line 141:
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= # Usage: # $(Q)$(MAKE) $(build)=dir build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
Kbuild.include被頂層Makefile所包含
頂層Makefile Line 312:
# We need some generic definitions (do not try to remake the file).
$(srctree)/scripts/Kbuild.include: ;
include $(srctree)/scripts/Kbuild.include
二、vmlinux的生成
頂層Makefile Line 845: 
# vmlinux image - including updated kernel symbols vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE ifdef CONFIG_HEADERS_CHECK $(Q)$(MAKE) -f $(srctree)/Makefile headers_check endif ifdef CONFIG_SAMPLES $(Q)$(MAKE) $(build)=samples endif ifdef CONFIG_BUILD_DOCSRC $(Q)$(MAKE) $(build)=Documentation endif $(call vmlinux-modpost) $(call if_changed_rule,vmlinux__) $(Q)rm -f .old_version
  vmlinux的依賴是「$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE」,要想生成vmlinux必需要先把這些原材料準備好。

<1> vmlinux-lds

  在編譯以前要把鏈接腳本先生成,最後的鏈接階段會用的着。

頂層Makefile Line 699: 
vmlinux-lds := arch/$(SRCARCH)/kernel/vmlinux.lds

<2> vmlinux-init

頂層Makefile Line 696: 
vmlinux-init := $(head-y) $(init-y)

  ①head-y

arch/arm/Makefile Line 100: 
head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
②init-y
頂層Makefile Line 477:
 init-y        := init/

頂層Makefile Line 661: 
init-y        := $(patsubst %/, %/built-in.o, $(init-y))

  init-y最終等於init/built-in.o

<3>vmlinux-main

頂層Makefile Line 697: 
vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)

① core-y

arch/arm/Makefile Line 196: 
core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

arch/arm/Makefile Line 197: 
core-y += $(machdirs) $(platdirs)

頂層Makefile Line 481: 
core-y        := usr/

頂層Makefile Line 650:
 core-y        += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

頂層Makefile Line 662:
 core-y        := $(patsubst %/, %/built-in.o, $(core-y))

  core-y最終等於

core-y := usr/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o \
crypto/built-in.o block/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o

② libs-y

arch/arm/Makefile Line 204:
 libs-y                := arch/arm/lib/ $(libs-y)

頂層Makefile Line 480: 
libs-y        := lib/

頂層Makefile Line 665:
 libs-y1        := $(patsubst %/, %/lib.a, $(libs-y))

頂層Makefile Line 666:
 libs-y2        := $(patsubst %/, %/built-in.o, $(libs-y))

頂層Makefile Line 667: 
libs-y        := $(libs-y1) $(libs-y2)

  libs-y最終等於arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o

③drivers-y

頂層Makefile Line 478:
 drivers-y    := drivers/ sound/ firmware/

頂層Makefile Line 663:
 drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y))

  drivers-y值最終等於rivers/built-in.o sound/built-in.o firmware/built-in.o

④net-y

頂層Makefile Line 479: 
net-y        := net/

頂層Makefile Line 664: 
net-y        := $(patsubst %/, %/built-in.o, $(net-y))

  net-y最終等於net/built-in.o

<4> vmlinux.o

  目前還未分析

<5> kallsyms.o

  目前還未分析

<6> FORCE

  它的目的是強迫重建vmlinux(不管上邊的原材料是否已經構建,不管上邊的原材料是否比已經生成的目標新,都要重建),這種用法在Linux Makefile體系中常常見到。

PHONY += FORCE
FORCE:

# Declare the contents of the .PHONY variable as phony.  We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)

  能夠看到FORCE爲一個僞目標,因此不管如何都要重建vmlinux。

 <7> 經過編譯查看最終的依賴文件

root@daneiqi:/workspace/linux-EmbedSky# make uImage V=1
  arm-linux-ld -EL  -p --no-undefined -X --build-id -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o 
  arch/arm/kernel/init_task.o  init/built-in.o --start-group  usr/built-in.o  arch/arm/kernel/built-in.o  
  arch/arm/mm/built-in.o  arch/arm/common/built-in.o  arch/arm/mach-s3c2410/built-in.o  
  arch/arm/mach-s3c2400/built-in.o  arch/arm/mach-s3c2412/built-in.o  arch/arm/mach-s3c2440/built-in.o  
  arch/arm/mach-s3c2442/built-in.o  arch/arm/mach-s3c2443/built-in.o  arch/arm/plat-s3c24xx/built-in.o  
  arch/arm/plat-s3c/built-in.o  kernel/built-in.o  mm/built-in.o  fs/built-in.o  ipc/built-in.o  security/built-in.o  
  crypto/built-in.o  block/built-in.o  arch/arm/lib/lib.a  lib/lib.a  arch/arm/lib/built-in.o  lib/built-in.o  
  drivers/built-in.o  sound/built-in.o  firmware/built-in.o  net/built-in.o --end-group .tmp_kallsyms2.o

  命令中的參數V=1的含義是「頂層Makefile Line 36: # Use 'make V=1' to see the full commands」。

三、vmlinux-lds、vmlinux-init、vmlinux-main的生成

<1>  vmlinux-dirs

頂層Makefile Line 871:
 $(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;

  vmlinux的主要構成文件vmlinux-init、vmlinux-main、vmlinux-lds的依賴是vmlinux-dirs。

頂層Makefile Line 652:vmlinux-dirs    := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
             $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
             $(net-y) $(net-m) $(libs-y) $(libs-m)))

  filter首先過濾掉不是文件夾的(帶「/」的就是文件夾),而後patsubst將全部的文件夾後邊的「/」去掉。筆者在頂層Makefile中添加一個僞目標Debug,使它打印vmlinux-dirs,而後在終端上執行命令"make Debug"獲得vmlinux-dirs的值爲:

init usr arch/x86/kernel arch/x86/mm arch/x86/crypto arch/x86/vdso kernel mm fs ipc security crypto block drivers sound firmware net lib arch/x86/lib

  可見vmlinux-dirs都是源碼包原本存在的文件夾,怎麼經過這些文件夾的名字來生成它們目錄下的*.built-in.o、lib.a呢?

<2> vmlinux-dirs對應的命令

頂層Makefile Line 879: 
PHONY += $(vmlinux-dirs) $(vmlinux
-dirs): prepare scripts $(Q)$(MAKE) $(build)=$@

  vmlinux-dir所表明的目錄都是僞目標,這樣即便這些文件夾是存在的,也要執行它們對應的命令。

① prepare

 在正式開始編譯以前,要先將準備工做作好,檢查輸出目錄是否與源碼目錄分開、若是分開了要建立這樣的目錄,還要生成include/config/kernel.release、include/linux/version.h include/linux/utsrelease.h 等文件。

如下代碼都是摘自頂層Makefile
Line 955: PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3 Line 957: # prepare3 is used to check if we are building in a separate output directory, Line 961: prepare3: include/config/kernel.release Line 975: # prepare2 creates a makefile if using a separate output directory Line 976: prepare2: prepare3 outputmakefile Line 978: prepare1: prepare2 include/linux/version.h include/linux/utsrelease.h \ Line 982: archprepare: prepare1 scripts_basic Line 984: prepare0: archprepare FORCE $(Q)$(MAKE) $(build)=. $(Q)$(MAKE) $(build)=. missing-syscalls Line 989: prepare: prepare0

②scripts

  準備工做還包括scripts,這些腳本用來決定編譯時的選項,例如include/config/auto.conf。

如下內容摘自頂層Makefile
Line 384: # Basic helpers built in scripts/ PHONY += scripts_basic scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic Line 472: PHONY += scripts scripts: scripts_basic include/config/auto.conf
③$(Q)$(MAKE) $(build)=$@

   編譯命令是相當重要的一句話,這句話就是來調用通用子Makefile文件--scripts/Makefile.build。$@會依次vmlinux-dirs中的文件夾代替,從而依次執行命令"make -f scripts/Makefile.build obj=$@"

編譯該目錄中的文件以生成*.built-in.o、lib.a等文件。

  咱們以$@爲drivers爲例進行如下內容的說明,下邊這條命令最終的結果是生成drivers/built-in.o。

make -f scripts/Makefile.build obj=drivers

2 scripts/Makefile.build的第一次調用階段  

  轉戰到scripts/Makefile.build,obj=drivers被帶到該文件,首先看一下這個文件都包含了哪些文件

一、Makefile.build的包含文件

如下內容皆來自於scripts/Makefile.build
Line 34: 
-include include/config/auto.conf

Line 36: 
include scripts/Kbuild.include

Line 41: 
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

Line 52: 
include scripts/Makefile.lib

Line 61: 
# Do not include host rules unless needed
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif

  包含文件有依據.config生成的配置文件include/config/auto.conf、include scripts/Kbuild.include、

$(kbuild-file)(也就是子目錄的Makefile或者kbuild)、include scripts/Makefile.lib、include scripts/Makefile.host(只在必定狀況下才被包含進入)。

  這裏着重講一下kbuild-file值的變化過程,kbuild-dir的等式中有filter函數,它的目的是將不是以「/」開頭的目錄濾除,顯然咱們的src=drivers,因此就被濾除掉,這個函數的值爲空。

scripts/Makefile.build Line 5: 
src := $(obj)
  $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))的結果最終等於$(srctree)/$(src),也就是給src添加絕對路徑。因此kbuild-dir值等於/workspace/linux-2.6.30.4/drivers。
  kbuild-file右邊的等式,首先查看在kbuild-dir目錄中是否有Kbuild存在,若是有就等於這個文件,不然使用這個目錄中的Makefile文件。kbuild-file最終等於/workspace/linux-2.6.30.4/drivers/Makefile。

二、scripts/Makefile.build的總目標

scripts/Makefile.build Line 7: 
PHONY := __build
__build:

scripts/Makefile.build Line 93:
 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
     $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
     $(subdir-ym) $(always)
    @:

 <1> KBUILD_BUILTIN

  在終端以#Make uImage執行Make時,KBUILD_BUILTIN := 1。

頂層Makefile Line 241:
KBUILD_BUILTIN := 1 # If we have only "make modules", don't compile built-in objects. # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to # make sure the checksums are up to date before we record them. ifeq ($(MAKECMDGOALS),modules) KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1) endif

頂層Makefile Line 264: 
export KBUILD_MODULES KBUILD_BUILTIN

 <2> builtin-target

scripts/Makefile.build Line 85: 
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif

  builtin-target值爲drivers/built-in.o

 <3> lib-target

scripts/Makefile.build Line 81: 
ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
lib-target := $(obj)/lib.a
endif

  lib-target的值爲drivers/lib.a

<4> $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) 

  初步認識時定義編譯的模塊

 <5> subdir-ym

subdir-ym的定義

scripts/Makefile.lib Line 38: 
__subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y)))
subdir-y    += $(__subdir-y)
__subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m)))
subdir-m    += $(__subdir-m)
scripts/Makefile.lib Line 47: 
subdir-ym := $(sort $(subdir-y) $(subdir-m))
scripts/Makefile.lib Line 89: 
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))

 __subdir-y和__subdir-m首先將obj-y、obj-m中的非文件夾濾除,而後經過patsubst函數將最後的「/」去除。注意到drivers/Makefile中的obj-y、obj-m統統都是文件夾。咱們以obj-y = net/爲例進行如下內容的說明,因此__subdir-y=net。

  subdir-ym就是在__subdir-y和__subdir-m前邊添加obj的前綴,因此最終等於drivers/net。

三、drivers/built-in.o的生成

   scripts/Makefile.build的總目標__build中的一個目標是 builtin-target,而它的值爲drivers/built-in.o,如今就來看看它是怎樣生成的。

scripts/Makefile.build Line 288: 
$(builtin-target): $(obj-y) FORCE
    $(call if_changed,link_o_target)

<1> drivers/built-in.o的依賴

  drivers/built-in.o依賴於obj-y(子目標),而後經過調用一個if_changed函數,將這些子目標鏈接起來,生成drivers/built-in.o。經過命令打印查看鏈接命令以下:

arm-linux-ld -EL    -r -o drivers/built-in.o drivers/gpio/built-in.o drivers/video/built-in.o 
    drivers/char/built-in.o drivers/gpu/built-in.o drivers/serial/built-in.o drivers/base/built-in.o 
    drivers/block/built-in.o drivers/misc/built-in.o drivers/mfd/built-in.o drivers/macintosh/built-in.o
    drivers/scsi/built-in.o drivers/net/built-in.o drivers/ieee1394/built-in.o drivers/cdrom/built-in.o
    drivers/auxdisplay/built-in.o drivers/mtd/built-in.o drivers/usb/built-in.o drivers/usb/gadget/built-in.o
    drivers/input/built-in.o drivers/rtc/built-in.o drivers/i2c/built-in.o drivers/media/built-in.o
    drivers/watchdog/built-in.o drivers/lguest/built-in.o drivers/idle/built-in.o drivers/mmc/built-in.o 
    drivers/firmware/built-in.o drivers/crypto/built-in.o drivers/hid/built-in.o drivers/platform/built-in.o     

  看下邊的代碼,原來drivers/Makefile中的obj-y是一羣目錄,經過第一行代碼後就給這些目錄後邊加上了built-in.o,再通過第二行代碼,給它們冠之前綴。咱們以obj-y=net/爲例說明,通過第一行代碼obj-y=net/built-in.o,通過第二行代碼obj-y=drivers/net/built-in.o

scripts/Makefile.lib Line 42:
obj-y := $(patsubst %/, %/built-in.o, $(obj-y))
scripts
/Makefile.lib Line 78:
obj-y := $(addprefix $(obj)/,$(obj-y))

<2> if_changed函數

  經過if_changed函數,完成了把這些子目錄中的目標鏈接生成dirvers/built-in.o。 

Kbuild.include Line 192: 
if_changed = $(if $(strip $(any-prereq) $(arg-check)),                       \
    @set -e;                                                             \
    $(echo-cmd) $(cmd_$(1));                                             \
    echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd)

  既然是函數,就會傳遞參數。相對於C語言來講形參就是$(1),而傳遞實參的方法就是 $(call if_changed,link_o_target),其中call是調用函數命令,if_changed是調用函數名,後邊的link_o_target就是實參。

①$(if $(strip $(any-prereq) $(arg-check))
any-prereq檢查是否有依賴比目標新,或者依賴尚未建立;arg-check檢查編譯目標的命令相對上次是否發生變化。若是二者中只要有一個發生改變,if_changed的值就等於
 @set -e;                                                             \
    $(echo-cmd) $(cmd_$(1));                                             \
    echo 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd

   不然,if_changed的值爲空,也就是說$(call if_changed,link_o_target)將什麼也不執行,由於不必執行。

②set -e 是說下邊的命令若是出錯了就直接返回,再也不繼續執行

③echo-cmd

Kbuild.include Line 156: 
echo-cmd = $(if $($(quiet)cmd_$(1)),\
    echo '  $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)

  echo-cmd就是打印出調用的命令。if $($(quiet)cmd_$(1))是判斷命令$(quiet)cmd_$(1)或者cmd_$(1)是否認義,也就是quiet_cmd_link_o_target或者cmd_link_o_target是否認義。若是有定義,echo-cmd就會將這個命令打印出來,也就是打印。

④$(cmd_$(1))

  對於 $(call if_changed,link_o_target),該命令就是cmd_link_o_target。

scripst/Makefile.build Line 283: 
cmd_link_o_target = $(if $(strip $(obj-y)),\
              $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
              $(cmd_secanalysis),\
              rm -f $@; $(AR) rcs $@)
$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) ,這個就是鏈接命令。

總結:

  if_changed函數的核心功能就是判斷是否須要更新目標,若是須要就執行表達式$(cmd_$(1))展開後的值來完成重建目標。

四、drivers/net/built-in.o
   drivers/built-in.o的依賴如何生成呢,好比說drivers/net/built-in.o,還記得subdir-ym嗎?subdir-ym記錄了當前目錄裏邊要參與編譯鏈接的子文件夾。在 「2 scripts/Makefile.build階段  二、scripts/Makefile.build的總目標 <5> $(subdir-ym)」中,咱們假定subdir-ym=drivers/net。

Makefile.build Line 356: 
PHONY += $(subdir-ym) $(subdir-ym): $(Q)$(MAKE) $(build)=$@
  $(Q)$(MAKE) $(build)=$@就是再一次調用scripts/Makefile.build,並傳遞參數drivers/net,這和生成drivers/built-in.o沒什麼差異。 

3 scripts/Makefile.build的再一次調用階段

  再次轉戰到scripts/Makefile.build,obj=drivers/net被帶到該文件。

一、Makefile.build的包含文件

  其包含文件與第一次調用階段沒什麼大的區別,最爲重要的區別就是包含子目錄Makefile的改變,此次包含的文件是/workspace/linux-2.6.30.4/drivers/net/Makefile。 

二、scripts/Makefile.build的總目標

  總目標中的builtin-target值爲drivers/net/built-in.o

三、drivers/net/built-in.o的生成

scripts/Makefile.build Line 288: 
$(builtin-target): $(obj-y) FORCE
    $(call if_changed,link_o_target)

  因爲子目錄的Makefile變成了/workspace/linux-2.6.30.4/drivers/net/Makefile,因此obj-y對應的發生了變化。經過執行編譯查看這個鏈接過程。

arm-linux-ld -EL    -r -o drivers/net/built-in.o drivers/net/mii.o drivers/net/Space.o 
drivers/net/loopback.o drivers/net/dm9000.o drivers/net/arm/built-in.o drivers/net/e1000/built-in.o

<1> drivers/net/built-in.o的依賴

  從drivers/net/Makefile中摘出兩個典型的obj-y組成部分以下所示:

drivers/net/Makefile Line 5: 
obj-$(CONFIG_E1000) += e1000/

drivers/net/Makefile Line 230: 
obj-$(CONFIG_DM9000) += dm9000.o

  drivers/net/built-in.o的依賴包含了文件夾,而且包含了直接由C文件生成的目標文件.o。

scripts/Makefile.lib Line 42:
obj-y        := $(patsubst %/, %/built-in.o, $(obj-y))

scripts/Makefile.lib  Line 78: 
obj-y        := $(addprefix $(obj)/,$(obj-y))

  第一行代碼讓obj-y若是是文件夾,就添加built-in.o,若是是普通的目標文件*.o,什麼也不操做;第二行代碼冠以目錄obj。因此obj-y最終等於drivers/net/e1000/built-in.o、drivers/net/dm9000.o。

  咱們愈來愈接近最終的目標了--分析到最底層的C文件生成目標文件。

<2> if_changed函數

  drivers/net/built-in.o也是一個由衆多的文件組成的庫文件,因此也是經過調用if_changed函數完成鏈接。

四、drivers/net/dm9000.o

scripts/Makefile.build Line 226:
# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c FORCE
    $(call cmd,force_checksrc)
    $(call if_changed_rule,cc_o_c)

scripts/Makefile.build Line 263:
$(obj)/%.o: $(src)/%.S FORCE
    $(call if_changed_dep,as_o_S)
$(obj)/%.o: $(src)/%.c FORCE是編譯C程序,$(obj)/%.o: $(src)/%.S FORCE是編譯彙編程序

<1> drivers/net/dm9000.o的依賴

  drivers/net/dm9000.o的依賴是對應的$(src)/%.c,也就是drivers/net/dm9000.c。

<2>cmd函數

scripts/Kbuild Line 160: 
cmd = @$(echo-cmd) $(cmd_$(1))

  此函數還未作深刻分析。

<2>if_changed_rule函數

 
 
scripts/Kbuild Line 205: 
# Usage: $(call if_changed_rule,foo)
# Will check if $(cmd_foo) or any of the prerequisites changed,
# and if so will execute $(rule_foo).
if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ),                 \
    @set -e;                                                             \
    $(rule_$(1)))
   $(call if_changed_rule,cc_o_c),從而會調用函數if_changed_rule,接着會執行命令$(rule_$(1))),也就是rule_cc_o_c。
scripts/Makefile.build Line 215:  
define rule_cc_o_c
    $(call echo-cmd,checksrc) $(cmd_checksrc)              \
    $(call echo-cmd,cc_o_c) $(cmd_cc_o_c);                  \
    $(cmd_modversions)                          \
    $(cmd_record_mcount)                          \
    scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
                                                  $(dot-target).tmp;  \
    rm -f $(depfile);                          \
    mv -f $(dot-target).tmp $(dot-target).cmd
endef

  其中$(cmd_cc_o_c)命令的定義是

scripts/Makefile.build Line 179: 
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

五、drivers/net/e1000/built-in.o的生成

  subdir-ym確定包含有文件夾drivers/net/e1000,因而繼續再一次調用scripts/Makefile.build來完成drivers/net/e1000/built-in.o的生成。 

Makefile.build Line 356: 
PHONY += $(subdir-ym)
$(subdir-ym):
    $(Q)$(MAKE) $(build)=$@

 3、Linux內核總體編譯過程

  通過解析過程的分析,編譯過程就是解析過程的相反操做。

一、生成準備文件

①控制C程序的頭文件

  include/linux/version.h include/linux/utsrelease.h、include/linux/autoconf.h

②控制編譯鏈接的文件

  arch/arm/kernel/vmlinux.lds、include/config/auto.conf等文件。

二、 由C程序源碼和彙編語言源碼生成目標文件(*.o)

三、將目標文件鏈接成*.built-in.o、*/lib.a等文件

四、將緊接着頂層目錄的子目錄中的*.built-in.o以及部分重要的*.o文件鏈接生成vmlinux

五、根據arch/arm/Makefile的規則生成zImage、uImage等

4、Linux內核編譯構成元素

一、Makefile的目標

<1> 總目標

  總目標其實是在arch/arm/Makefile中定義了,比方說zImage、uImage,頂層Makefile緊接着定義了這些終極目標直接的依賴目標vmlinux。

<2> 各級子目標

  各級子目標是在scripts/Makefile.build中的__build中定義的,例如傳遞參數obj=drivers後的目標是drivers/built-in.o。

  這些目標的依賴其實又成爲了新的目標,例如drivers/net/built-in.o、drivers/net/dm9000.o。

二、Makefile的依賴

<1> 總目標的依賴

  vmlinux-lds  vmlinux-init vmlinux-main vmlinux.o kallsyms.o

<2> 各級子目標的依賴

  各級子目標的依賴是由子目錄中的Makefile(實際是scripts/Makefile.build的包含文件)和scripts/Makefile.lib共同完成肯定的。

  子目錄中的Makefile負責選材,而scripts/Makefile.lib負責加工。

三、Makefile的規則

<1> 總目標的鏈接規則

  總目標vmlinux的鏈接規則就是在頂層Makefile中定義的,至於zImage、uImage則是在arch/arm/Makefile中定義的。

<2>子目標的編譯鏈接規則

  主要是在scripts/Makefile.build、scripts/Kbuild.include中定義的,其中scripts/Kbuild.include定義了許多諸如if_changed的函數。

四、Makefile的編譯鏈接選項

  本文並無討論。

5、Linux內核Makefile特色

一、兩個Makefile

 頂層Makefile文件負責將各個目錄生成的*.built-in.o、lib.a等文件鏈接到一塊兒生成vmlinux。而scripts/Makefile.build 包含子目錄中的Makefile文件以及scripts/中的衆多腳原本生成這些*.built-in.o、lib.a、*.o等文件。

  經過「make -f scripts/Makefile.build obj=」的方法完成了頂層Makefile到scripts/Makefile.build的調用生成*/built-in.o,以及scripts/Makefile.build的自調用生成更低一級子目錄的*/built-in.o。

二、編譯的目錄始終是頂層目錄

  「make -C」命令會先切換工做目錄,而後執行該目錄中的Makefile,u-boot就是採用這種方法。而linux則是利用「make -f」的方法,因此編譯的目錄始終是頂層目錄。

三、通用規則

  Linux內核Makefile的通用子Makefile是scripts/Makefile.build,而通用的其餘規則則是scripts中的其餘文件。

 

參考資料:《深度探索Linux操做系統:系統構建和原理解析》

相關文章
相關標籤/搜索