學習目標 分析Makefile文件,瞭解內核中的哪些文件被編譯,如何被編譯,連接時順序如何確定! Linux內核源碼中包含很多的Makefile文件,這些Makefile文件又包含其它的一些文件,比如配置信息、通用規則等等。我們可以把內核中的Makefile文件分為5類,如下表所示: 配置文件,在執 ...
學習目標
分析Makefile文件,瞭解內核中的哪些文件被編譯,如何被編譯,連接時順序如何確定!
Linux內核源碼中包含很多的Makefile文件,這些Makefile文件又包含其它的一些文件,比如配置信息、通用規則等等。我們可以把內核中的Makefile文件分為5類,如下表所示:
頂層Makefile | 所有Makefile文件的核心,從總體控制內核的編譯、連接 |
.config |
配置文件,在執行配置命令時生成。所有Makefile文件都根據.config來決定如何使用哪些文件 |
arch/$(ARCH)/Makefile | 對應CPU體繫結構的Makefile文件,用來決定那些體繫結構相關的文件參與內核的生成,並提供一些規則來生成特定格式內核映象 |
kbuild Makefile | 各級子目錄下的Makefile,相對比較簡單,被上一級Makefile調用來編譯當前目錄下的文件 |
script/Makefile.* | Makefile共用規則和腳本 |
執行make uImage命令時內核最終被編譯,所以要分析內核Makefile文件關係,把目標uImage作為分析的入手點最為合適。打開頂層Makefile文件,在頂層Makefile文件中查找目標uImage的依賴關係,結果發現在頂層Makefile文件中不能直接找到目標uImage的依賴。雖然在頂層Makefile文件中沒有直接找到目標uImage的依賴關係,但我們執行make uImage命令時內核被編譯,所以我們可以推測目標uImage肯定是在其它目錄下的Makefile中定義,並且這個目錄下的Makefile文件被頂層的Makefile文件所包含。查找其它目錄下的Makefile文件,最終在arch/arm目錄下Makefile文件中找到目標uImage的依賴關係,代碼如下:
227 zImage Image xipImage bootpImage uImage: vmlinux #來源於arch/arm目錄下的Makefile文件 228 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
為了驗證上面推測是否正確,我們在頂層Makefile中搜索"include"字元,找到頂層Makefile中所有包含文件。通過篩選後找到下麵所示代碼:
413 include $(srctree)/arch/$(ARCH)/Makefile #來源於頂層Makefile中文件 414 export KBUILD_DEFCONFIG ........ 185 #ARCH ?= $(SUBARCH) 186 ARCH ?= arm 187 CROSS_COMPILE ?= arm-linux-
上述代碼$(srctree)=源碼根目錄、$(ARCH)=arm,include $(srctree)/arch/$(ARCH)/Makefile就表示頂層Makefile包含arch/arm目錄下Makefile,由此可見前面我們的推測是正確的。
從上面227行代碼中可以發現,目標uImage依賴於vmlinux。uImage文件有兩部分組成,頭部和真正內核部分,要獲得uImage文件必須先編譯出內核,也就是先生成vmlinux文件,最後根據vmlinux文件生成uImage文件。下麵我們還要找出vmlinux的依賴關係,在頂層Makefile中找到vmlinux的依賴關係如下代碼:
745 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE #來源於頂層的Makefile
可以看出vmlinux生成依賴於$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o),因此為了進一步分析,我們要分別找出$(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o)變數代表的內容,並對變數所代表內容進行一一展開。
608 vmlinux-init := $(head-y) $(init-y) #來源於頂層Makefile文件 94 head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o #來源於arch/arm目錄下的Makefile文件
443 init-y := init/ #來源於頂層Makefile文件
573 init-y := $(patsubst %/, %/built-in.o, $(init-y)) #來源於頂層Makefile文件
第94行$(MMUEXT)內容為空,head$(MMUEXT).o就是head.o, head-y := arch/arm/kernel/head.o arch/arm/kernel/init_task.o
第573行$(patsubst <pattern>,<replacement>,<text> ) 是一個Makefile函數,查找<text>中的單詞是否符合模式<pattern>,如果匹配的話,則以<replacement>替換。這裡,<pattern>可以包括通配符“%”,表示任意長度的字串。如果<replacement>中也包含“%”,那麼,<replacement>中的這個“%”將是<pattern>中的那個“%”所代表的字串。init-y :=init/built-in.o
第608行將$(head-y) $(init-y)用94行和573行分析結果替換,可以得到vmlinux-init :=arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o
609 vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y) #來源於頂層Makefile文件
438 core-y := usr/ #來源於頂層Makefile文件
562 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ #來源於頂層Makefile文件
574 core-y := $(patsubst %/, %/built-in.o, $(core-y)) #來源於頂層Makefile文件
437 libs-y := lib/ #來源於頂層Makefile文件
577 libs-y1 := $(patsubst %/, %/lib.a, $(libs-y)) #來源於頂層Makefile文件
578 libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y)) #來源於頂層Makefile文件
579 libs-y := $(libs-y1) $(libs-y2) #來源於頂層Makefile文件
435 drivers-y := drivers/ sound/ #來源於頂層Makefile文件
575 drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y)) #來源於頂層Makefile文件
436 net-y := net/ #來源於頂層Makefile文件
576 net-y := $(patsubst %/, %/built-in.o, $(net-y)) #來源於頂層Makefile文件
結合438行和562行,574行,第574行core-y := 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
結合437行、577行和578行,第579行libs-y := lib/lib.a lib/built-in.o
結合435行,第575行drivers-y := drivers/built-in.o sound/built-in.o
結合436行,第576行net-y :=net/built-in.o
第609行將$(core-y) $(libs-y) $(drivers-y) $(net-y)用574行、579行、575行和576分析結果替換,可以得到vmlinux-main :=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 lib/lib.a lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o
610 vmlinux-all := $(vmlinux-init) $(vmlinux-main)
結合608行和609行分析結果,將610行$(vmlinux-init) $(vmlinux-main)替換,可以得到vmlinux-all :=arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/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 lib/lib.a lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o
以上介紹了構成內核的原材料,但它們怎樣組合編譯、連接成為內核,我們還要去看vmlinux依賴文件下執行的命令
745 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE #頂層Makefile文件 746 ifdef CONFIG_HEADERS_CHECK 747 $(Q)$(MAKE) -f $(srctree)/Makefile headers_check 748 endif 749 $(call if_changed_rule,vmlinux__) 759 $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@ 751 $(Q)rm -f .old_version
我們可以通過分析上面命令得到編譯過程以及生成的一些文件,但上面命令涉及很多函數、腳本,實在太過龐大,沒有那麼多精力去這樣做。這時可以通過另一種方法直接編譯內核,先通過rm vmlinux命令刪去之前執行make uImage生成的vmlinux,再執行make uImage V=1查看更加詳細編譯過程,連接命令如下圖所示:
圖中vmlinux為連接輸出結果,arch/arm/kernel/vmlinux.lds為連接腳本,後面的一些內容為連接的原材料,這些原材料和我們上面分析的原材料也基本一一對應。
總結:
1、頂層Makefile和arch/$(ARCH)/Makefile決定根目錄下哪些子目錄、arch/$(ARCH)目錄下哪些文件和目錄被編進內核,最後,各級子目錄下的Makefile決定所在目錄下哪些文件將被編進內核,哪些文件被編成模塊,進入哪些子目錄繼續調用它們的Makefile。
2、通過分析得到了編譯內核的連接腳本是arch/arm/kernel/vmlinux.lds,第一個文件是arch/arm/kernel/head.S,後續分析內核啟動時,第一個文件arch/arm/kernel/head.S將是分析入手點。