刘庆敏 博客linux,Linux内核源码分析--zImage出生实录(Linux-3.0 ARMv7)
內核根目錄下的vmlinux映像文件是內核Makefile的默認目標。這個vmlinux映像的生成可以通過閱讀內核Makefile文件得知,簡單的說:Makefile解析內核配置文件.config,遞歸到各目錄下編譯出.o文件,最后將其鏈接成vmlinux。而這個鏈接成的vmlinux文件是一個包含內核代碼的靜態可執行ELF文件,你可以通過file命令來驗證這一點。她不能通過bootloader引導并啟動,如果想要使其可引導,必須使用編譯工具鏈中的objcopy命令把這個ELF格式的vmlinux轉化為二進制格式才行。
而平常使用的zImage文件就是這個vmlinux文件經過多次的轉換得到的。現在就來仔細研究一下她的生成過程。
(1)arch/$(ARCH)/Makefile
首先嵌入式中經常使用的編譯目標zImage并不在頂層Makefile文件中,而在被頂層Makefile包含的arch/$(ARCH)/Makefile文件中,對于ARM處理器來說就是arch/arm/Makefile文件。其中的部分規則如下:
……
# Default target when executing plain make
ifeq ($(CONFIG_XIP_KERNEL),y)
KBUILD_IMAGE := xipImage
else
KBUILD_IMAGE := zImage
endif
all:$(KBUILD_IMAGE)
boot := arch/arm/boot
archprepare:
$(Q)$(MAKE) $(build)=arch/arm/tools include/generated/mach-types.h
# Convert bzImage to zImage
bzImage: zImage
zImage Image xipImage bootpImage uImage:vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
……
從這里可以看出,zImage的依賴是頂層vmlinux文件,下面的命令展開得到:
make -f scripts/Makefile.build obj= arch/arm/boot MACHINE=arch/arm/mach-* arch/arm/boot/ zImage
可以看出zImage其實是make解析arch/arm/boot目錄下的Makefile文件生成的,而參數傳遞了目標芯片信息和目標“arch/arm/boot/zImage”。所以zImage其實是在arch/arm/boot目錄下完成編譯的,這就是為什么可引導zImage映像會在arch/arm/boot目錄下。
(2)arch/$(ARCH)/boot/Makefile
現在來分析一下arch/arm/boot/Makefile中的部分規則,看看目標zImage的生成:
$(obj)/Image: vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
$(obj)/compressed/vmlinux: $(obj)/Image FORCE
$(Q)$(MAKE) $(build)=$(obj)/compressed $@
$(obj)/zImage:$(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
@echo ' Kernel: $@ is ready'
先看最后一行,從中可以得知arch/arm/boot/zImage的依賴目標是arch/arm/boot/ compressed/vmlinux,且目標zImage是其二進制化的產物。
而arch/arm/boot/compressed/vmlinux是如何得到的呢?再看上一規則,arch/arm/boot/compressed/vmlinux的依賴目標是arch/arm/boot/Image。這個依賴目標的生成由最上面的規則決定,顯然arch/arm/boot/Image是由頂層vmlinux二進制化得到的。而中間這行規則的含義是arch/arm/boot/compressed/vmlinux由make解析arch/arm/boot/compressed/目錄下的Makefile文件生成的,這條命令展開得到:
make -f scripts/Makefile.build obj= arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux
(3)arch/$(ARCH)/boot/compressed/Makefile
最后就來分析一下arch/arm/boot/compressed/Makefile中的部分規則,看看arch/arm/boot/compressed/vmlinux 的生成:
......
suffix_$(CONFIG_KERNEL_GZIP) = gzip
suffix_$(CONFIG_KERNEL_LZO) ?= lzo
suffix_$(CONFIG_KERNEL_LZMA) = lzma
......
$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \
$(addprefix $(obj)/, $(OBJS)) $(lib1funcs) FORCE
$(call if_changed,ld)
@$(check_for_bad_syms)
$(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE
$(call if_changed,$(suffix_y))
$(obj)/piggy.$(suffix_y).o: $(obj)/piggy.$(suffix_y) FORCE
......
上面的第一條規則就說明了:其實arch/arm/boot/compressed/vmlinux是由幾個部分根據arch/arm/boot/compressed/vmlinux.lds 腳本鏈接而成的:
$(obj)/$(HEAD):arch/arm/boot/compressed/head.o,在鏈接時處于vmlinux的最前面,其主要作用就是做一些必要的初始化工作,如初始化CPU、中斷描述符表IDT 和內存頁目錄表GDT等等,最后跳到misc.c中的decompress_kernel函數進行內核的自解壓工作。
$(addprefix $(obj)/, $(OBJS)):arch/arm/boot/compressed/ misc.o,位于head.o之后,是內核自解壓的實現代碼。
以下假定是gzip模式壓縮:
$(obj)/piggy.$(suffix_y).o:arch/arm/boot/compressed/ piggy.gzip.o,其實是arch/arm/boot/Image經過gzip壓縮后(piggy.gzip),再借助piggy.gzip.S一起編譯出的ELF可鏈接文件。其中的原理可以看看piggy.gzip.S源碼:
.section .piggydata,#alloc
.globlinput_data
input_data:
.incbin"arch/arm/boot/compressed/piggy.gzip"
.globlinput_data_end
input_data_end:
這里我還是要額外的提一下gzip壓縮,也就是$(call if_changed,$(suffix_y))這個過程。這個命令認真解析起來比較麻煩,這里如果有興趣的讀者可以自行分析。這里介紹兩篇經典的分析文檔:《kbuild實現分析》、《Kbuild系統原理分析》,讀者可自行上網下載學習。這里我直接給出了結果,這條命令執行了在Makefile.lib(scripts)中定義的:
cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || \
(rm -f $@ ; false)
也就是說,piggy.gzip是將arch/arm/boot/Image文件cat到標準輸出,并通過管道傳入gzip命令(gzip -n -f -9)的標準輸入,最后將gzip的輸出重定向到目標piggy.gzip
而這個piggy.gzip文件有一個重要的特性:最后的四個字節,是文件壓縮前的大小數據,存放格式是小端模式。這個數據在zImage自解壓時會被用于程序得到內核解壓后所需要的空間!!!
感興趣的朋友可以自己隨便用“gzip -n -f -9”壓縮一個文件試試,驗證一下,我已親自驗證過了。
這樣跟蹤下來,zImage的產生過程已經看完了,但是讀者可能會被這有點復雜的關系繞暈了,所以現在可以結合一下的流程圖簡單地總結一下:
首先頂層vmlinux是ELF格式的可執行文件,必須將其二進制化生成Image后才可以被bootloader引導。為了實現壓縮的內核映像,arch/arm/boot/compressed/Makefile又將這個非壓縮映像Image做gzip壓縮,生成了piggy.gzip。但要實現在啟動時自解壓,必須將這個piggy.gzip轉化為.o文件,并同初始化程序head.o和自解壓程序misc.o一同鏈接,生成arch/arm/boot/compressed/vmlinux。最后arch/arm/boot/Makefile將這個ELF格式的arch/arm/boot/compressed/vmlinux二進制化得到可被bootloader引導的映像文件zImage。
總結
以上是生活随笔為你收集整理的刘庆敏 博客linux,Linux内核源码分析--zImage出生实录(Linux-3.0 ARMv7)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多线程CreateThread函数的用法
- 下一篇: Boolean()