Linux Kernel 和 U-Boot 編譯的那些事

Linux Kernel and U-Boot compile

之前的文章:《一次搞定交叉編譯》 給大家講了如何安裝交叉編譯工具鏈,搭建交叉編譯環境。

這篇文章主要教大家如何正確的去編譯 Linux Kernel、U-Boot 這些著名的開源軟件。

也許很多同學會說:編譯是小 case 啊,我都 make 過成千上萬次了!

可是你是否有思考過,你編譯的時候每一個步驟、執行的每一個命令…

它背後隱藏的原理是什麼?

爲什麼要這麼做?

你的方法是最高效最科學的嗎?

你的方法是否潛藏着漏洞?

換一個環境、換一個平臺,如果編譯的過程中遇到了莫名其妙的錯誤,你是否知道從哪裏去找突破口?

這就是這篇文章要告訴你的。

編譯 Linux Kernel

還是以 i.MX 的內核爲例。

其實過程很簡單,基本上兩個命令搞定:

make ARCH=arm imx_v7_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

第一個命令是配置,第二個命令是編譯。

但是這背後卻隱藏着很多小細節需要我們去注意。

  1. 配置內核

配置內核的命令是 make ARCH=chiparch xxx_defconfig ,第一個參數 ARCH=chiparch 用來指定芯片的架構,第二個參數 xxx_defconfig 用來指定這次編譯對應的配置文件,比如針對 i.MX6、i.MX7 這些芯片的 Linux Kernel,配置命令是這樣的:

make ARCH=arm imx_v7_defconfig

如果編譯 i.MX8 相關的內核的話,配置命令大概是這樣的:

 make ARCH=arm64 defconfig

這個命令會從指定的 defconfig 文件裏面加載配置,寫入到 .config 文件中。內核編譯的時候就是根據 .config 文件的內容來決定哪些模塊編譯,哪些模塊不編譯的。

make 後面的兩個參數是怎麼來的?

因爲 Linux Kernel 支持大量不同架構的芯片、比如 arm、arm64、x86、mips、risc-v 等等,還支持成千上萬的功能特性,在編譯的時候我們並不需要把內核支持的所有芯片和功能都編譯進去,這樣編譯出來的內核鏡像會非常的龐大。而且 Kernel 中有相當一部分代碼是針對特定架構的,比如啓動階段的初始化代碼、每個架構都有自己特定的設置,這部分代碼大部分是彙編寫的,而且相互不兼容:

所以,Linux 內核提供了 defconfig 機制 ,開發者們可以根據自己的芯片、開發板特性自己決定內核中哪些功能需要打開,哪些功能可以關閉,生成一個和自己硬件相關的 defconfig 文件,下次編譯的時候加載。這些 defconfig 文件放在 arch/chiparch/configs 下面:

所以在配置的時候,我們需要指定具體的 ARCH、Kernel 的 Kbuild 系統才能在對應的目錄下找到你指定的 defconfig 文件。如果沒有指定 ARCH、一般默認會去 x86 目錄下去找。

因爲 i.MX6、 i.MX7 是 Arm32 ,所以對應的 ARCH 爲 arm,i.MX8 是 Arm64,所以對應的 ARCH 爲 arm64.

另外需要說明一點的是,大家可以看到 arm 和 mips 目錄下 defconfig 文件非常多,而比較新的一些架構,比如arm64,risc-v 目錄下,只有唯一的一個 defconfig 文件,這和 Linux Kernel 目前的開發風格轉變有關:不再鼓勵大家提交一堆亂七八糟的 defconfig 文件, 儘量只使用一個通用的 defconfig 文件,這裏面儘量打開內核啓動需要的模塊,而對內核啓動影響不大的模塊,以模塊的形式編譯。在這個設計思想下,Arm32 下面有一個通用的 multi_v7_defconfig, Arm64 和 risc-v 對應的 defconfig 文件名都叫做 defconfig。所以如果現在你想爲一個新的芯片提交它自己的 defconfig 文件到 mainline 分支,是不會被接受的,Linux 社區的 Maintainer 會告訴你,把你需要打開的特性加到通用的 defconfig 裏面。

當然,我們在本地做開發的時候,一般不會這樣玩,我們還是會根據自己的芯片和特定產品形態,創建獨立的 defconfig 文件,這樣方便深度裁剪。

這個配置文件是如何生成的呢?

我們一般在一個現有配置文件的基礎上,根據產品需求,通過 make menuconfig 命令加減配置,然後再通過 make savedefconfig 命令生成新的配置文件:

比如我需要打開 drivers/gpu/drm/imx/dw_hdmi-imx.c 這個 HDMI 驅動,通過查看該 C 文件同目錄下 Makefile,可以發現它依賴 DRM_IMX_HDMI 這個配置項:

make ARCH=arm menuconfig

對於對內核還不怎麼熟悉的同學來說,如何找到 DRM_IMX_HDMI 這個配置的位置呢?別急,menuconfig 界面也是可以搜索關鍵字的:

/ 鍵,就是 ? 下面那個鍵,會彈出下面的界面:

然後在選擇框裏面輸入要查找的關鍵字,敲 Enter 就會出現結果:

這裏只有一個匹配的選項,所以我們直接在鍵盤上按 1 鍵,就會跳到對應的選項開關處:

然後打開對應的選項即可,<*> 號表示直接編譯進內核, 表示以模塊的方式編譯,< > 則表示不編譯。

上下移動光標,按 光標在最下面左右移動。

配置完成後選 退出。這時候可以看到 DRM_IMX_HDMI 這個選項的配置已經生效了:

然後執行 make savedefconfig 命令,生成新的配置文件,並用該文件覆蓋舊文件。

有人可能會疑問,爲什麼要用 make savedefconfig 來生成一箇中間的 defconfig 文件呢,直接用 .config 去覆蓋不是也可以嗎?

答案是: make savedefconfig 命令生成的 defconfig 文件更精簡,更易讀。

  1. 編譯內核

    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
    

    這是最通用的編譯內核的命令,第一個告訴內核要編譯 arm 架構,第二個參數指定用什麼交叉編譯工具去編譯。編譯成功的結果大概是這樣的:

最終編譯後的鏡像是壓縮過的 zImage。

同時所有的 dts 文件也會被編譯成 dtb。

當然,如果爲了編譯的快,我們還可以啓動並行編譯,比如:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8

就是同時有 8 個編譯進程在運行,並行數目設置多少最合適,這個一般取決與你用來編譯的電腦有多少 CPU,以及內存夠不夠大,一般數據可以設置的和 CPU 個數相等,或者 2 倍。

有人會說,爲什麼我看到的有些開發板的編譯說明文檔和這個不太一樣,比如下面這個:

這是因爲,部分廠家的Kernel,爲了滿足自己的固件升級設計,做了一些特殊的打包和修改,你雖然看到的編譯命令不一樣了,其實他們都是基於最基本的編譯命令做的封裝定製。

它在 Makefile 裏面指定了 ARCH 和 CROSS_COMPILE, 所以make 的時候就不需要指定這兩個參數了。

.

然後通過這個修改,當開發者執行 make xxx.img 這種模式的命令的時候,會自動編譯 zImage,並通過mkkrnlimg 這個命令對 zImage 打包生成 kernel.img。 並且對通過 resource_tool 這個命令對 dtb 也進行了打包。

Linux Kbuid 系統還執行很多其他的 make 命令,可以通過 make help 來查看:

編譯 U-Boot

U-Boot 的編譯步驟和 Linux Kernel 非常類似,也是兩步:

make  mx6ull_14x14_evk_defconfig
make CROSS_COMPILE=arm-linux-gnueabihf-

唯一的差別是,U-Boot 在編譯的時候不需要指定 ARCH 選項,這是 U-Boot 的編譯系統相比 Linux Kbuid 的要給改進點。其實目前最新的 U-Boot 編譯系統也是基於 Linux Kbuild 設計的,同樣支持 make menuconfig 命令,有對應的 defconfig 文件,在 configs 目錄下。

另外一個區別是,U-Boot 也有自己的 dtb,但是最終編譯完的 U-Boot 會和 dtb 合併在一起。

編譯遇到錯誤怎麼辦

我們在編譯軟件的時候,經常會遇到各種奇奇怪怪的報錯,有些是軟件本身有 Bug(對於 Linux Kernel、U-Boot 這類比較知名的開源項目,這種Bug 比較少見)、有些是因爲編譯環境裏面缺少一些依賴工具(這種情況很常見)。

對於剛接觸的新人來說,一條 make 命令敲下去,發現蹦出來一堆莫名其妙的錯誤,是很令人沮喪的。

但是不要怕,雖然你是新人,這也沒關係,因爲現在是互聯網時代,對於那些廣泛使用的開源項目,你踩到的坑基本都有很多人踩到過,而且這個世界有那麼多樂於分享的人:他們遇到問題,勇敢的去分析,尋求解決方法,然後分享出來,他們是這個數字世界的熱和光。

所以,如果你遇到一個自己能看得懂搞得定的問題,那很簡單,直接去搞定即可,然後如果你樂意,能再寫一篇分享心得就更好了。如果你遇到了一個像天書一樣,自己完全看不懂的報錯,也不要緊,最實用的解決方案就是:直接把這個錯誤複製出來,粘貼到你的搜索引擎裏,點下下一步,開始搜索即可,一條條耐心的去看,一般運氣都不會太差。

當然,搜索引擎的選擇也是一門學問,如果你用到是百度,能搜到答案最好,如果搜不到,你還可以試試 Bing、Stackoverflow、Github、如果你能用 Google,那就更好了。

相信我,這個方法能解決你所遇到的大部分問題。

比如 我在執行 make ARCH=arm imx_v7_defconfig 就提示 failed 了:

仔細分析這個日誌,我們會發現最開始有這樣一個異常提示:/bin/sh: 1: bison: not found

一般在分析異常的時候,比較有效的方法是找到最開始報出異常的地方,先解決掉,然後再看是不是最終問題被解決掉了,其實大部分時候都是這樣的,把最先冒頭的問題解決掉,後面所有的問題都被打掉了。

如果你能看懂這個異常提示,其實你會發現它是說 shell 在執行 bison 這個命令的時候,發現這個命令找不到。

試試在命令行執行下 bison 這個命令,會發現如下提示:

然後按照提示 sudo apt install bison 安裝即可解決。

如果你看不出來,也沒關係,用前面提到的終極解決方案,直接把這條提示覆制到百度裏搜搜看:

運氣比較好,太多人和我踩了同樣的坑,前三條連接任意打開一個,點進去都能找到答案:

sudo apt install bison  

安裝完後再次執行 make ARCH=arm imx_v7_defconfig,bison 的報錯沒了,可是又蹦出來另外一個錯誤:

和前面一樣,還是直接 複製——》粘貼——》搜索

踩坑的不止我一個啊,打開任意一個鏈接,就能找到解決辦法:

sudo apt install flex

然後再次執行 make ARCH=arm imx_v7_defconfig,終於成功了。

然後執行 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-開始編譯, 發現又報錯了:

直接用前面提到的三部曲:複製——》粘貼——》搜索

打開這些鏈接,你會找到答案:

sudo apt install libssl-dev

再次開始編譯,好不容易編譯到最後,又挑出來一個錯誤:

相信到這裏你已經知道該怎麼做了,沒錯,還是 複製——》粘貼——搜索:並且你會很快找到解決問題的方法:

sudo apt-get install lzop

繼續編譯,終於成功了:

其實這篇文章與其說是講述 Linux Kernel 和 U-Boot 的編譯步驟,不如說是告訴大家一種在開發過程中,遇到自己不懂的問題,如何去利用網絡來尋找答案的方法,希望能給你一點啓發。

更多原創請掃碼關注公衆號:HackforFun

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章