交叉编译busybox和Linux kernel 并在qemu上运行

转载请注明出处

原文地址: 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
list.png

编译内核

编译内核之前,先安装依赖

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

移除模块支持

menu1.png

在 Kernel Features 中打开 EABI支持

menu2.png

step3: 编译

make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- all

编译的时候会报一个错误:

err1.png

这里的解决办法是 kernel/timeconst.pl 的373 行做如下修改

correct1.png

然后重新执行上面的编译命令。编译成功后会在 源码目录下 arch/arm/boot/下面生成一个叫做 zImage的文件。这个就是内核文件了。

下面使用qemu启动内核了。

安装 qemu

sudo apt install qemu-system-arm 

启动内核

qemu-system-arm -M versatilepb -m 128M -kernel zImage

应看到如下启动信息

qemu-no-rootfs.png

可以看到最后有一行打印了 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"

可以看到如下

qemu-init.png

至此,我们已经完成了上面的教程。下面额外添加编译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"

将会看到如下:

qemu-busybox.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。