轉(zhuǎn)載請注明出處
原文地址: http://www.itdecent.cn/p/5aaea4d41aa6
@簡書
@earth_xmx
翻譯部分
原文地址https://jasonblog.github.io/note/arm_emulation/compiling_linux_kernel_for_qemu_arm_emulator.html
上一次,我編譯了無操作系統(tǒng)的ARM程序(bare-metal ARM programs)和U-boot. 本節(jié),我們將在ARM平臺上編譯一個 linux kernel. 由于我沒有真實的ARM設(shè)備, 所以本節(jié)我用QEMU模擬ARM設(shè)備。
linux內(nèi)核代碼和QEMU都支持VersatilePB平臺。所以,我選擇VersatilePB作為測試平臺。交叉編譯工具鏈,我選擇 CodeSourcery ARM EABI toolchain.
內(nèi)核代碼可以從 http://kernel.org 上下載. 我選擇最新的版本(version 2.6.33)并且解壓縮到想關(guān)的目錄。
在內(nèi)核根代碼中執(zhí)行:
make ARCH=arm versatile_defconfig
上面的命令,使用了預(yù)定義的配置(arch/arm/configs/versatile_defconfig)。 這個配置讓內(nèi)核在VersatilePB板子上正常運行。執(zhí)行上面的命令后,會把versatilepb的默認配置寫入到根目錄下的 .config 文件中。另外,執(zhí)行如下命令在.config 的基礎(chǔ)上進行微調(diào)配置。
make ARCH=arm menuconfig
我移除了模塊支持,并且打開了EABI支持(同時支持 old ABI)。對于使用 CodeSourcery 交叉編譯工具編譯出的程序,其要想正常運行。這個選項必須要打開。
執(zhí)行如下命令,進行編譯。
make ARCH=arm CROSS_COMPILE=arm-none-eabi- all
如果使用GNU/Linux 工具鏈,應(yīng)該執(zhí)行如下進行編譯:
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
上面便于命令結(jié)束且沒有報錯的時候,會在 arch/arm/boot 下生成了一個 叫 zImage的文件。這個文件可以被 QEMU使用。
qemu-system-arm -M versatilepb -m 128M -kernel zImage
上面的命令:QEMU將執(zhí)行zImage。內(nèi)核會顯示很多的啟動信息。最后會顯示它找不到文件系統(tǒng)。所以,我們下一步創(chuàng)建一個簡單的文件系統(tǒng)。此文件系統(tǒng)僅包含一個"Hello world"可執(zhí)行程序。
#include <stdio.h>
void main() {
printf("Hello World!\n");
while(1);
}
注意:程序最后要使用無限循環(huán)保證程序不退出。因為 linux 內(nèi)核執(zhí)行的第一個程序后,內(nèi)核期望地一個程序永遠都不結(jié)束,不然會包 kernel panic錯誤。
arm-none-linux-gnueabi-gcc -static test.c -o test
上面的命令會創(chuàng)建一個靜態(tài)鏈接的二進制程序(靜態(tài)代表其依賴的所有庫都鏈接到一個單一的可執(zhí)行程序里面了)。下面就可以創(chuàng)建文件系統(tǒng)了。
echo test | cpio -o --format=newc > rootfs
cpio 工具會從標準輸入中讀取一系列文件列表,并且會在標準輸出中輸出一個格式為newc的歸檔文件。我們這里把標準輸出重定向到一個名為 rootfs的文件中。newc文件系統(tǒng)格式,是linux內(nèi)核能夠直接識別的文件系統(tǒng)。(而ext4這樣的文件系統(tǒng),需要內(nèi)核加載相應(yīng)的模塊才能識別。)rootfs文件是一個文件系統(tǒng)的鏡像文件,其中只包含一個文件(test可執(zhí)行文件)。 QEMU可以把 rootfs文件傳給內(nèi)核(傳入機制以后再說)。通過下面的命令啟動內(nèi)核。
qemu-system-arm -M versatilepb -m 128M -kernel zImage -initrd rootfs -append
"root=/dev/ram rdinit=/test"
#include <stdio.h>
void main() {
printf("Hello World!\n");
while(1);
}
實踐部分
下面的實踐在 ubuntu 22.04 中進行。上面原文的作者大概是在2010年左右整理出來的教程?,F(xiàn)在都2022年了。希望可以按照他的教程,完成內(nèi)核的編譯與運行。
先下載 linux 內(nèi)核源碼,我這里選擇的是2.6.34.1 跟原文有點小差別。
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.34.1.tar.bz2
tar xf linux-2.6.34.1.tar.bz2
如果下載失敗,可以打開上面的網(wǎng)址,找一個差不多的內(nèi)核應(yīng)該也行。
部分
下載安裝交叉編譯工具鏈并配置(參考文章:https://developer.ridgerun.com/wiki/index.php/Code_Sourcery_ARM_toolchain_2011.09)
wget https://sourcery.mentor.com/public/gnu_toolchain/arm-none-linux-gnueabi/arm-2011.09-70-arm-none-linux-gnueabi.bin
chmod +x arm-2011.09-70-arm-none-linux-gnueabi.bin
## 安裝依賴:
sudo apt install lib32z1 ## 32位lib,如果是ubuntu其他版本,可能是安裝其他包,請自行搜索解決。
## 執(zhí)行安裝程序(如果執(zhí)行失敗,請查看終端輸出的錯誤log,然后解決)
./arm-2011.09-70-arm-none-linux-gnueabi.bin
## 一路按照提示,安裝完成。
安裝完后配置環(huán)境變量:(我的安裝目錄為~/CodeSourcery/)
export PATH=$PATH:~/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin

編譯內(nèi)核
編譯內(nèi)核之前,先安裝依賴
sudo apt install make
sudo apt install gcc
sudo apt install libncurses5-dev
編譯內(nèi)核: step1: 先使用versatile的默認配置去寫入.config文件
cd linux-2.6.39.2/
make ARCH=arm versatile_defconfig
step2: 使用menuconfig 調(diào)整.config文件使內(nèi)核支持 EABI
make ARCH=arm menuconfig
移除模塊支持

在 Kernel Features 中打開 EABI支持

step3: 編譯
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
編譯的時候會報一個錯誤:

這里的解決辦法是 kernel/timeconst.pl 的373 行做如下修改

然后重新執(zhí)行上面的編譯命令。編譯成功后會在 源碼目錄下 arch/arm/boot/下面生成一個叫做 zImage的文件。這個就是內(nèi)核文件了。
下面使用qemu啟動內(nèi)核了。
安裝 qemu
sudo apt install qemu-system-arm
啟動內(nèi)核
qemu-system-arm -M versatilepb -m 128M -kernel zImage
應(yīng)看到如下啟動信息

可以看到最后有一行打印了 not syncing: VFS: unable to mount root fs on unkown ....
下面創(chuàng)建內(nèi)核要啟動的第一個進程 init
#include <stdio.h>
void main() {
int num = 1;
for (num = 1; num <= 10; ++num)
printf("Hello World![num=%d]\n", num);
while(1);
}
編譯并制作rootfs
arm-none-linux-gnueabi-gcc -static init.c -o init
echo init | cpio -Hnewc -o > rootfs.img
重新啟動內(nèi)核
qemu-system-arm -M versatilepb -m 128M -kernel arch/arm/boot/zImage -initrd ../test/rootfs.img -append "rdinit=/init"
可以看到如下

至此,我們已經(jīng)完成了上面的教程。下面額外添加編譯busybox到rootfs中去。
busybox編譯到initrd中
下載busybox 源碼
https://www.busybox.net/downloads/busybox-1.20.0.tar.bz2
tar xf busybox-1.20.0.tar.bz2
編譯busybox源碼
cd busybox-1.20.0
make ARCH=arm defconfig ## 使用默認配置
make ARCH=arm menuconfig ## 打開靜態(tài)編譯選項,把busybox編譯成static形勢,如同上面的init程序。
### Busybox Settings -> Build Options -> Build BusyBox as a static binary (no shared libs)
## 不打開的話一會還要拷貝動態(tài)鏈接庫到rootfs中,就比較麻煩。
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- install #編譯源碼
上述編譯如果沒有錯誤的話,會在 busybox源碼目錄下生成一個 _install 的目錄。
制作 rootfs
cd _install
find . | cpio -Hnewc -o | gzip -9 > ../rootfs.img.gz
## rootfs.img.gz 文件保存在 busybox-1.20.0 下面了。
使用qemu加載啟動
qemu-system-arm -M versatilepb -m 128M -kernel arch/arm/boot/zImage -initrd ../busybox-1.20.0/rootfs.img.gz -append "rdinit=/bin/sh"
將會看到如下:
