我們應該瞭解,核心編譯出vmlinux還不算數,最後這個ELF格式的檔案會被壓縮成bzImage。不過那無關緊要。這次我要說說vmlinux的產生流程,當然只是簡單走走,裡面kbuild複雜文法,連Makefile內建的很多規則都是比較複雜的,恐怕要全部搞懂,得費很大勁,畢竟這對於我研究的方向沒有多大協助。我們知道要產生可執行檔要經過這樣幾個步驟:xx.c->xx.o、xx.o->xx,即先編譯在連結,最終xx就是我們要的檔案,那麼核心原始碼怎麼有序的形成這樣的模式呢,靠的就是kbuild這個了不起的架構模型。
當然第一步找到vmlinux目標。
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
這個目標依賴的還挺多的,不過這裡我只關注2個:vmlinux-init、vmlinux-main。可以看出前面兩個是變數。只分析一個即可。
找到vmlinux-init。
vmlinux-init := $(head-y) $(init-y)
這裡又有兩個變數:head-y、init-y。有些熟悉,有點類似obj-y、obj-m等吧。通過尋找,我沒找到啊,當然這個Makefile是沒有了,這個是和機器有關的變數,在這行上面不遠處,有包含:
include $(srctree)/arch/$(SRCARCH)/Makefile
現在應該很熟悉了吧,無非就是將關於機器的那個Makefile給包含進來,這裡以x86為例。找到head-y的蹤影。
head-y := arch/x86/kernel/head_$(BITS).o
head-y += arch/x86/kernel/head$(BITS).o
head-y += arch/x86/kernel/head.o
head-y += arch/x86/kernel/init_task.o
好了,怎麼找,應該知道了。不過這裡還來看看init-y會發現和head-y有些不同。至於像.o這些檔案怎麼產生的,後面會看到。
init-y := init/
這是第一次定義,但是別太慌,下面還有。
init-y := $(patsubst %/, %/built-in.o, $(init-y))
這句話翻譯一下就是 init-y := init/built-in.o。這個build-in.o這個中間檔案可能聽說過,其實這個檔案就是有這個目錄下所有obj-y來合成的。當然會用到遞迴調用的方式,不過怎麼個遞迴法不是我要探討的問題,我只需要找到在哪裡開始的就ok。好了,vmlinux-init基本就可以確定了,下面的vmlinux-main類似方式可以確定。我在來說說比較關鍵的一個問題,build-in.o是在哪條命令中產生,因為我們知道肯定得涉及kbuild規則遞迴調用才能做到。其實要知道這個在哪裡產生的確得看些東西才能找到,不過這裡我只是簡單說說就可以了。知道這句話就可以了。
$(sort $(vmlinux-init) $(vmlinux-main)) $(vmlinux-lds): $(vmlinux-dirs) ;
這句在執行vmlinux目標前由依賴關係產生,我們來看看最關鍵的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)))
其實$(vmlinux-dirs)還是一個目標。
PHONY += $(vmlinux-dirs)
$(vmlinux-dirs): prepare scripts
$(Q)$(MAKE) $(build)=$@
翻譯一下,就知道用到了Makefile.kbuild規則。將vmlinux-dirs裡面的目錄進行了遞迴處理產生出我們之前說的那些.o的檔案,特別是build-in.o
如果不瞭解一些常識,誰會注意到sort那句話就是幹這事情的。所以在執行vmlinux時候,$(vmlinux-dirs)目標肯定會先得到執行,現在該有的都有了,vmlinux-init、vmlinux-main,關鍵的內容都來,最後執行連結即可得到vmlinux,在哪裡連結的,看看endif後面那3條。
endif
$(call vmlinux-modpost)
$(call if_changed_rule,vmlinux__)
$(Q)rm -f .old_version
第一條就是調用一下vmlinux-modpost,這個其實也是個命令,只不過是自訂的。尋找一下,在上面。
quiet_cmd_vmlinux-modpost = LD $@
cmd_vmlinux-modpost = $(LD) $(LDFLAGS) -r -o $@ /
$(vmlinux-init) --start-group $(vmlinux-main) --end-group /
$(filter-out $(vmlinux-init) $(vmlinux-main) FORCE ,$^)
define rule_vmlinux-modpost
:
+$(call cmd,vmlinux-modpost)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost $@
$(Q)echo 'cmd_$@ := $(cmd_vmlinux-modpost)' > $(dot-target).cmd
endef
發現沒有,既定義了命令還定義了規則,$(LD)就是連結了。呵呵,說得夠簡單的,說到這裡發現,整個過程竟然如此簡單,但是要分析到這些得看很多內容的,發現我搞2天呢。呵呵,不還是蠻值得的。