樹莓派官方提供的Raspberry Pi OS固件中,boot分區內核的鏡像文件命名為kernel7.img,而ubuntu官方提供的固件中,boot分區內核的鏡像文件則為vmlinuz,這兩者有什么區別?如何生成對應的鏡像文件?
這里,我們有必要了解,常見的嵌入式系統中,內核鏡像的格式有哪些,如何生成和使用。
1、內核鏡像格式
1.1 vmlinux
vmlinux是整個內核編譯的直接產物。這里提及的是“編譯”。顯然,內核主要是C語言實現,編譯器對內核編譯,同樣是一般的編譯過程:預處理、編譯、匯編和鏈接。從文件的角度,它是ELF格式文件。比如我們通過gcc對*.c進行編譯得到的可執行文件,就是屬於ELF格式文件。這里vm代表Virtual Memory,Linux支持虛擬內存,因此得名vm。該文件一般在生成在內核源碼根目錄下。
通過readelf工具查看vmlinux的文件頭信息:
readelf -h vmlinux:
ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: DYN (Shared object file) Machine: AArch64 Version: 0x1 Entry point address: 0xffffffc010080000 Start of program headers: 64 (bytes into file) Start of section headers: 15358864 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 5 Size of section headers: 64 (bytes) Number of section headers: 32 Section header string table index: 29
以上可以看到,該內核鏡像文件是在aarch64上運行的。
1.2 Image
在編譯完內核時候,有如下打印信息:
OBJCOPY arch/arm64/boot/Image
的確,Image就在上面的目錄下。那么OBJCOPY是什么操作?我們來看看實際執行的命令:
./arch/arm64/boot/.Image.cmd:1:cmd_arch/arm64/boot/Image := aarch64-linux-gnu-objcopy -O binary -R .note -R .note.gnu.build-id -R .comment -S vmlinux arch/arm64/boot/Image
該命令的解析可以參照如下:
objcopy -O binary -R .note -R .comment -S *.elf *.bin #使用 -O binary (或--out-target=binary) 輸出為原始的二進制文件 #使用 -R .note (或--remove-section) 輸出文件中不要.note這個section,縮小了文件尺寸 #使用 -R .comment(或--remove-section) 輸出文件中不要.comment這個section,縮小了文件尺寸 #使用 -S (或 --strip-all)輸出文件中不要重定位信息和符號信息,縮小了文件尺寸
也即:Image是在vmlinux基礎上,去掉不必要信息的二進制碼(binary)
1.3 Image.gz
在編譯樹莓派64內核時,在boot目錄下,也會發現該鏡像文件。在編譯過程,有:
GZIP arch/arm64/boot/Image.gz
實際上,Image.gz就是通過GZIP壓縮工具對上述的Image進行壓縮的產物。在編譯時,也可以指定make image.gz。注意到,樹莓派的Ubuntu提供的固件,其boot區存放的是vmlinuz,其實就是該壓縮的Image,只不過是重命名為vmlinuz罷了。因此,也可以手動來生產樹莓派ubuntu-arm64對應的內核鏡像:
gzip Image #執行后,Image將會變成Image.gz
cp Image.gz vmlinuz
arm64是在linux 3.7版本及之后方導入的arch,也是至此之后,內核生成aarch64的內核鏡像都是通過gzip直接對Image壓縮的,這和arm(32)的內核鏡像不一樣(詳見后述)。如下是官方的說明:
The AArch64 kernel does not currently provide a decompressor and therefore requires decompression (gzip etc.) to be performed by the boot loader if a compressed Image target (e.g. Image.gz) is used. For bootloaders that do not implement this requirement, the uncompressed Image target is available instead.
也即:內核鏡像vmlinuz/Image.gz,不再(像zImage)那樣帶有自解壓代碼,而是需要BootLoader去完成該部分工作,否則,就只能直接使用非解壓的Image鏡像。
1.4 zImage
我們在編譯樹莓派32位內核時,會有如下打印信息,基本可以宏觀上看到整個生成過程。
LD vmlinux.o MODPOST vmlinux.o KSYM .tmp_kallsyms1.o KSYM .tmp_kallsyms2.o LD vmlinux SORTEX vmlinux SYSMAP System.map OBJCOPY arch/arm/boot/Image Kernel: arch/arm/boot/Image is ready LDS arch/arm/boot/compressed/vmlinux.lds AS arch/arm/boot/compressed/head.o GZIP arch/arm/boot/compressed/piggy_data CC arch/arm/boot/compressed/misc.o CC arch/arm/boot/compressed/decompress.o CC arch/arm/boot/compressed/string.o AS arch/arm/boot/compressed/hyp-stub.o AS arch/arm/boot/compressed/lib1funcs.o AS arch/arm/boot/compressed/ashldi3.o AS arch/arm/boot/compressed/bswapsdi2.o AS arch/arm/boot/compressed/piggy.o LD arch/arm/boot/compressed/vmlinux OBJCOPY arch/arm/boot/zImage Kernel: arch/arm/boot/zImage is ready
首先,zImage是基於Image生成的,和Image.gz不一樣的地方是,zImage包含了自解壓部分代碼,並且不是直接壓縮的產物。上面log也看到,piggy_data是在Image基礎上壓縮的,類似於Image.gz,同時與head.o/misc.o等,鏈接成vmlinux(顯然該compress目錄下的vmlinux不同於boot目錄下的vmlinux)。最后,通過OBJCOPY,同樣去掉一些不必要信息(符號、注釋、調試信息等),得到最終的zImage。該鏡像可以通過make zImage來生成。
因此,zImage是帶有自解壓代碼的內核鏡像文件。
1.5 bzImage
bzImage是通過make bzImage命令來生成,其生成過程與zImage一樣。如果內核比較小,那么可以采用zImage或bzImage之一,兩種方式引導的系統運行 時是相同的。大的內核采用bzImage,不能采用zImage。
1.6 uImage
uImage,從名字看,可以簡單理解為,給uboot使用的內核鏡像。具體地,它是使用uboot工具mkimage對普通的壓縮內核映像文件(zImage)加工而得。具體怎樣加工?很簡單,就是在zImage之前添加64個字節的頭,說明這個內核的版本、加載位置、生成時間、大小等信息,這些信息供uboot加載內核的時候使用。
2、總結
內核編譯,首先得到的是vmlinux的ELF文件,可以通過OBJCOPY命令來去掉不必要的信息(符號、注釋、調試等),得到Image。在64位內核中,通過GZIP對Image直接壓縮,得到Image.gz,在一些系統中,還重命名為vmlinuz。由於不帶解壓程序,因此內核的加載之前,還需要BootLoader去做解壓。對32位內核,通常使用帶自解壓程序的zImage,該鏡像是Image基礎上進行壓縮,然后與head.o/misc.o鏈接,再經過OBJCOPY的獲取到的鏡像,因此不是簡單的GZIP壓縮(也不能通過GZIP來解壓使用)。
根據前面的分析,我們將各類鏡像的生成過程通過如下圖直觀表示出來:
回到本文開始提到的問題,您是否得到答案了呢?