转载请注明出处
原文地址: https://www.jianshu.com/p/5aaea4d41aa6
@简书
@earth_xmx
翻译部分
原文地址https://jasonblog.github.io/note/arm_emulation/compiling_linux_kernel_for_qemu_arm_emulator.html
上一次,我编译了无操作系统的ARM程序(bare-metal ARM programs)和U-boot. 本节,我们将在ARM平台上编译一个 linux kernel. 由于我没有真实的ARM设备, 所以本节我用QEMU模拟ARM设备。
linux内核代码和QEMU都支持VersatilePB平台。所以,我选择VersatilePB作为测试平台。交叉编译工具链,我选择 CodeSourcery ARM EABI toolchain.
内核代码可以从 http://kernel.org 上下载. 我选择最新的版本(version 2.6.33)并且解压缩到想关的目录。
在内核根代码中执行:
make ARCH=arm versatile_defconfig
上面的命令,使用了预定义的配置(arch/arm/configs/versatile_defconfig)。 这个配置让内核在VersatilePB板子上正常运行。执行上面的命令后,会把versatilepb的默认配置写入到根目录下的 .config 文件中。另外,执行如下命令在.config 的基础上进行微调配置。
make ARCH=arm menuconfig
我移除了模块支持,并且打开了EABI支持(同时支持 old ABI)。对于使用 CodeSourcery 交叉编译工具编译出的程序,其要想正常运行。这个选项必须要打开。
执行如下命令,进行编译。
make ARCH=arm CROSS_COMPILE=arm-none-eabi- all
如果使用GNU/Linux 工具链,应该执行如下进行编译:
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
上面便于命令结束且没有报错的时候,会在 arch/arm/boot 下生成了一个 叫 zImage的文件。这个文件可以被 QEMU使用。
qemu-system-arm -M versatilepb -m 128M -kernel zImage
上面的命令:QEMU将执行zImage。内核会显示很多的启动信息。最后会显示它找不到文件系统。所以,我们下一步创建一个简单的文件系统。此文件系统仅包含一个"Hello world"可执行程序。
#include <stdio.h>
void main() {
printf("Hello World!\n");
while(1);
}
注意:程序最后要使用无限循环保证程序不退出。因为 linux 内核执行的第一个程序后,内核期望地一个程序永远都不结束,不然会包 kernel panic错误。
arm-none-linux-gnueabi-gcc -static test.c -o test
上面的命令会创建一个静态链接的二进制程序(静态代表其依赖的所有库都链接到一个单一的可执行程序里面了)。下面就可以创建文件系统了。
echo test | cpio -o --format=newc > rootfs
cpio 工具会从标准输入中读取一系列文件列表,并且会在标准输出中输出一个格式为newc的归档文件。我们这里把标准输出重定向到一个名为 rootfs的文件中。newc文件系统格式,是linux内核能够直接识别的文件系统。(而ext4这样的文件系统,需要内核加载相应的模块才能识别。)rootfs文件是一个文件系统的镜像文件,其中只包含一个文件(test可执行文件)。 QEMU可以把 rootfs文件传给内核(传入机制以后再说)。通过下面的命令启动内核。
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年左右整理出来的教程。现在都2022年了。希望可以按照他的教程,完成内核的编译与运行。
先下载 linux 内核源码,我这里选择的是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
如果下载失败,可以打开上面的网址,找一个差不多的内核应该也行。
部分
下载安装交叉编译工具链并配置(参考文章: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其他版本,可能是安装其他包,请自行搜索解决。
## 执行安装程序(如果执行失败,请查看终端输出的错误log,然后解决)
./arm-2011.09-70-arm-none-linux-gnueabi.bin
## 一路按照提示,安装完成。
安装完后配置环境变量:(我的安装目录为~/CodeSourcery/)
export PATH=$PATH:~/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin
编译内核
编译内核之前,先安装依赖
sudo apt install make
sudo apt install gcc
sudo apt install libncurses5-dev
编译内核: step1: 先使用versatile的默认配置去写入.config文件
cd linux-2.6.39.2/
make ARCH=arm versatile_defconfig
step2: 使用menuconfig 调整.config文件使内核支持 EABI
make ARCH=arm menuconfig
移除模块支持
在 Kernel Features 中打开 EABI支持
step3: 编译
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all
编译的时候会报一个错误:
这里的解决办法是 kernel/timeconst.pl 的373 行做如下修改
然后重新执行上面的编译命令。编译成功后会在 源码目录下 arch/arm/boot/下面生成一个叫做 zImage的文件。这个就是内核文件了。
下面使用qemu启动内核了。
安装 qemu
sudo apt install qemu-system-arm
启动内核
qemu-system-arm -M versatilepb -m 128M -kernel zImage
应看到如下启动信息
可以看到最后有一行打印了 not syncing: VFS: unable to mount root fs on unkown ....
下面创建内核要启动的第一个进程 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
重新启动内核
qemu-system-arm -M versatilepb -m 128M -kernel arch/arm/boot/zImage -initrd ../test/rootfs.img -append "rdinit=/init"
可以看到如下
至此,我们已经完成了上面的教程。下面额外添加编译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 ## 打开静态编译选项,把busybox编译成static形势,如同上面的init程序。
### Busybox Settings -> Build Options -> Build BusyBox as a static binary (no shared libs)
## 不打开的话一会还要拷贝动态链接库到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"
将会看到如下: