柴米油鹽計劃

Kbuild 編譯 Linux 內核系列(十)

Author: Wei Yang
作者公眾號:楊小偉的世界

真沒有想到,在編譯過內核的源碼目錄下,你可以找到兩個同叫 vmlinux 的文件。

$find . -name vmlinux
./vmlinux
./arch/x86/boot/compressed/vmlinux

怎麼樣,你之前有發現過么?這還是在探索 vmlinux.bin 的過程中發現的秘密。

他們究竟都是幹什麼用的?有什麼聯繫?和 bzImage 之間有關聯么?讓我們來揭開這神秘的面紗。

隱藏的vmlinux

老規矩,先來看看 vmlinux.bin 的規則。

$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
    $(call if_changed,objcopy)

嗯,看到剛才 find 中發現的 vmlinux 了不?原來 vmlinux.bin 是這個 vmlinux 通過 objcopy 而來。那這個都包含了誰? 又和根目錄下的 vmlinux 有什麼關係呢?你們倆真是太像了。

$(obj)/compressed/vmlinux: FORCE
    $(Q)$(MAKE) $(build)=$(obj)/compressed $@

原來老人家還有一個單獨的目錄,再次調用了 make 命令。

揭開面紗

檢驗基本功的時候又來了,還記得這個命令究竟是做了什麼么?還記得這個時候,規則文件是要去哪裡找么?如果想不起來可以去回顧前面幾篇入門文章。

arch/x86/boot/compressd/Makefile 中,找到了規則:

$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
    $(call if_changed,check_data_rel)
    $(call if_changed,ld)

偷個懶,猜一下,這個 vmlinux 就是把變數 vmlinux-objs-y 中的所有目標鏈接而成的。是不是覺得易如反掌了?

好,那來看看這個變數里都有誰。

vmlinux-objs-y := $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
    $(obj)/string.o $(obj)/cmdline.o $(obj)/error.o \
    $(obj)/piggy.o $(obj)/cpuflags.o

好少啊,感覺比 setup.bin 還少。貌似挺簡單的啊,這麼著就算是把整個 bzImage 的編譯流程都走完了~

真的完了么? 你有沒有發現什麼不對勁?

對了,我們的根目錄的 vmlinux 呢?難道放到啟動目錄下的內核裡面沒有根目錄的 vmlinux ?不對啊,根目錄的 vmlinux 可是包含了所有內核真正的代碼的啊。

是的,你發現的沒錯,我們還沒有真正走到內核編譯的最深處。

石破驚天

也不知道是什麼機緣巧合,竟然發現在 arch/x86/boot/compress/ 目錄下面的 piggy.S這 個文件是編譯時生成的。

在我的環境上,這個文件看上去像是這樣:

.section ".rodata..compressed","a",@progbits
.globl z_input_len
z_input_len = 6957106
.globl z_output_len
z_output_len = 22677744
.globl input_data, input_data_end
input_data:
.incbin "arch/x86/boot/compressed/vmlinux.bin.gz"
input_data_end:

怎麼樣,你有沒有覺得大跌眼鏡?簡直就是 FXXK 。

在彙編代碼中直接包含了一個文件。讓我們看看這個 incbin 語句是什麼含義吧。

You can use INCBIN to include executable files, literals, or any arbitrary data. The contents of the file are added to the current ELF section, byte for byte, without being interpreted in any way.

哥們直接把整個內核給包進來了,小生佩服。

再燒一次腦

為了確認,我們再來看看這個 vmlinux.bin.gz 是不是真的是內核的代碼。

注意,下面的關係還真是有點燒腦。

先來看 vmlinux.bin.gz :

vmlinux.bin.all-y := $(obj)/vmlinux.bin

$(obj)/vmlinux.bin.gz: $(vmlinux.bin.all-y) FORCE
    $(call if_changed,gzip)

那再來看 $(obj)/vmlinux.bin 。注意哦,這個已經是 arch/x86/boot/compressed/vmlinux.bin ,而不是 arxh/x86/boot/vmlinux.bin 了。怎麼樣,是不是有點燒腦?

$(obj)/vmlinux.bin: vmlinux FORCE
    $(call if_changed,objcopy)

而這個時候的依賴, vmlinux ,就是根目錄下的 vmlinux了 。

At last。

終於,我們看清楚了內核代碼是怎麼樣打包到 bzImage 中了。

一張圖解說

我知道,你肯定已經暈的不能再暈了。我們還是用一張圖來解說一下。

       vmlinux

         ||
         \/

       boot/compressed/vmlinux.bin


         ||
         \/

       boot/compressed/vmlinux.bin.gz

         ||
         \/

       piggy.o       head_$(BITS).o
                     misc.o
                     cmdline.o
                     error.o
                     cpuflsgs.o

             ||
             \/

      boot/compressed/vmlinux

             ||
             \/

         boot/vmlinux.bin

是不是稍微的清晰了那麼一些些?

真相大白

終於知道了內核還會被壓縮,還會被放在一個叫 piggy.o 的文件中,然後再被打包成一個 bzImage 放到啟動目錄中被載入。

沒想到內核的大俠們也挺逗的,起個名字盡然還有點冷幽默。

整個內核的編譯過程已經基本走完了,你可能會問知道這個編譯的過程除了好玩還能有什麼用呢?

我想除了能用來參加面試之外,可能還有一個原因,那就是你有機會明白內核啟動時的頁表是長什麼樣的。嗯?你不知道?不著急,你的內核之旅可能才剛剛開始。


本文是 LinuxStory 柴米油鹽計劃的投稿文章,由 LinuxStory 整理髮布。原作者署名為:Wei Yang 。轉載請註明出處,否則必究相關責任。
本文鏈接:https://linuxstory.org/kbuild-linux-10/

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
這裡是柴米油鹽計劃投稿的發布賬號。

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據