为 QEMU ARM 仿真器编译 Linux 内核:QEMU 模拟 ARM 环境

QEMU 是一套由法布里斯·贝拉(Fabrice Bellard)所编写的以 GPL 许可证分发源码的模拟处理器,在GNU/Linux 平台上使用广泛。简单来说,QEMU 是一个虚拟机,与常见的 Vmware/VirtualBox 不同的是,QEMU 可以模拟不同平台的硬件,使得我们在 x86 设备上可以运行其他架构的程序。

本文主要讲述如何编译符合 qemu 要求的内核,使用 qemu 成功运行内核。我们需要在 Linux 环境下安装交叉编译工具链,用于在 x86 平台上编译 arm 架构的内核;也要解决其中的依赖问题;如果有需要,还要升级系统自带的 qemu(系统自带版本较高,可以不用重新安装)。如果能够成功的运行内核,后续还可以在 qemu 模拟的环境中,挂载相应的根文件系统,这样就可以完整的运行一个 arm 架构的 linux 系统。

0x10 前期准备

0x11 编译内核需要的依赖及工具

下载配置内核需要的依赖

sudo apt-get install ncurses-devel libncurses-devel flex bison bc

安装交叉工具编译链

sudo apt-get install gcc-arm-linux-gnueabi
# 此工具用来编译生成 arm32 可执行程序

0x12 编译 QEMU

Ubuntu 以及 Kali 自带了 qemu,如果你想升级 qemu 的版本,可以源码安装。

安装依赖

sudo apt-get install zlib1g-dev libglib2.0-0 libglib2.0-dev libtool libsdl1.2-dev autoconf
sudo apt install libsdl2-dev -y   # 不安装可能会出现 VNC Server running ":1:5900"

解压

tar -xvf qemu-4.2.0.tar.xz

为了防止编译后文件比较乱,选择创建 build 目录作为编译中间目标路径

cd qemu-4.2.0/
mkdir build

配置、编译并安装 qemu

./configure --prefix=./build --target-list=arm-softmmu,arm-linux-user --enable-debug
make -j$(nproc)
make install

–target-list 指定要编译的target(guest),arm-softmmu 表示要编译 system mode 的 arm qemu;arm-linux-user 表示要编译 user mode 的 arm qemu。

如果要重新编译,请删除原先的编译中间文件

sudo make clean
sudo make distclean

查看 qemu 支持的开发板

qemu-system-arm -M help

0x20 编译内核

下载内核,这里推荐几个下载内核源码的镜像站点:
https://mirrors.edge.kernel.org/pub/linux/kernel/
http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/

0x21 方案一:编译生成与平台无关的内核

进入源码根目录,配置和编译

#(1)清除原有的配置与中间文件
  make distclean
#(2)配置内核,并生成配置文件
  make menuconfig ARCH=arm
#(3)编译内核
  make uImage ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all

编译内核的时候,既可以选择 uImage,也可以不填此选项,这样编译的时候,会提示用户,选择使用哪种内核压缩模式。笔者在这里选择的是 zImage,最后生成的文件如下

~/Documents/linux-5.6.6$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all
scripts/kconfig/conf  --syncconfig Kconfig
  CALL    scripts/checksyscalls.sh
  CALL    scripts/atomic/check-atomics.sh
  CHK     include/generated/compile.h
  Kernel: arch/arm/boot/Image is ready
  Kernel: arch/arm/boot/zImage is ready
  MODPOST 17 modules

也就是说,arch/arm/boot/zImage 就是我们编译好的内核。

0x22 方案二:编译生成特定开发板(qemu)运行的内核

使用 arch/arm/configs/versatile_defconfig 文件的配置,versatile_defconfig 的内容将被 copy 到 .config 中,这样生成的内核文件可以直接使用 qemu 进行仿真。

#(1)清除原有的配置与中间文件
  make distclean
#(2)配置内核,并生成配置文件
  make ARCH=arm versatile_defconfig # vexpress_defconfig
  make menuconfig ARCH=arm 
#(3)编译内核
  make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all

versatile_defconfig 是指 ARM Versatile Express 开发板的配置,我们也可以用其他硬件仿真。qemu -m 命令可查看当前 qemu 支持仿真的硬件平台,即开发板

lys@kali:~/Documents$ qemu-system-arm -machine help
Supported machines are:
akita                Sharp SL-C1000 (Akita) PDA (PXA270)
ast2500-evb          Aspeed AST2500 EVB (ARM1176)
ast2600-evb          Aspeed AST2600 EVB (Cortex A7)
borzoi               Sharp SL-C3100 (Borzoi) PDA (PXA270)
...
tosa                 Sharp SL-6000 (Tosa) PDA (PXA255)
verdex               Gumstix Verdex (PXA270)
versatileab          ARM Versatile/AB (ARM926EJ-S)
versatilepb          ARM Versatile/PB (ARM926EJ-S)
vexpress-a15         ARM Versatile Express for Cortex-A15
vexpress-a9          ARM Versatile Express for Cortex-A9
...

0x23 方案三:手动配置内核源码并编译

make vexpress_defconfig ARCH=arm O=./object
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig -j4 O=./object 

生成以下两个我们需要的文件

arch/arm/boot/zImage
arch/arm/boot/dts/vexpress-v2p-ca9.dtb

0x30 QEMU 加载内核

使用方案二生成的内核压缩文件

qemu-system-arm -M versatilepb -m 256M -kernel linux-5.6.6/arch/arm/boot/zImage -nographic -dtb linux-5.6.6/arch/arm/boot/dts/versatile-pb.dtb -append "console=ttyAMA0"

用方案三生成的内核压缩文件以及配置文件

qemu-system-arm -M vexpress-a9 -m 512M -kernel linux-5.6.6/arch/arm/boot/zImage -dtb linux-5.6.6/arch/arm/boot/dts/vexpress-v2p-ca9.dtb -append "console=ttyAMA0" -serial stdio

参数说明

  • -M 指定开发板
  • -m 指定内存大小
  • -kernel 指定内核文件
  • -dtb 指定dtb文件
  • -nographic 指定不需要图形界面
  • -append 指定扩展显示界面,串口或者LCD,“console=ttyAMA0” 内核启动参数,这里告诉内核vexpress单板运行,串口设备是哪个tty

0x31 不开启图形终端

为了在图形窗口中显示,我们需要传递 console=tty1 内核参数。这个内核参数将会被 qemu 通过 -append 选项传递给 Linux。如果不指定 -nographic,上述命令会打开 qemu 并打开一个黑色的控制台窗口,通过一个 Tuxlogo 来显示图形能力。启动信息将会在这个图形窗口显示。

内核成功启动,如下图,不过我们还没有指定根文件系统,所以报错
在这里插入图片描述

0x32 重定向模拟系统的串口

-nographic 是为了让系统直接输出,不要可视化界面,但是这样的话,要关闭 qemu 模拟的系统,只能通过 kill 方式。qemu 可以重定向主机上的模拟系统的串口,使用选项 -serial stdio,则 Linux 可以通过传递 console=ttyAMA0 作为内核参数而在第一个串口中显示它的信息。

qemu-system-arm -M versatilepb -m 256M -kernel linux-5.6.6/arch/arm/boot/zImage \
-dtb linux-5.6.6/arch/arm/boot/dts/versatile-pb.dtb \
-append "console=ttyAMA0" \
-serial stdio \

在这里插入图片描述

0x33 开启远程登录

-nographic 还可以用 -serial telnet::2020,server,nodelay 替代,这样可以另开一个终端,通过输入 telnet 127.0.0.1 2020 的方式连接到 qemu 虚拟机,此方法会有三个终端存在

emu-system-arm -M versatilepb -m 256M -kernel linux-5.6.6/arch/arm/boot/zImage \
-dtb linux-5.6.6/arch/arm/boot/dts/versatile-pb.dtb \
-append "console=ttyAMA0" \
-serial telnet::2020,server,nodelay

在这里插入图片描述

0x34 后续操作

后续就是加上 -initrd 参数选项指定固件中的根文件系统,就可以仿真固件。如果你觉得命令过于复杂,写出脚本就可以了

# boot.sh
#! /bin/sh
qemu-system-arm \
        -M vexpress-a9  \
        -m 512M \
        -kernel ~/qemu/zImage   \   
        -dtb ~/qemu/vexpress-v2p-ca9.dtb    \   
        -nographic  \
        -append "root=/dev/mmcblk0 rw console=ttyAMA0"    \
        -sd rootfs.ext3

0x40 内核挂载根文件系统

内核运行成功后,需要根文件系统的支撑,才能形成一个完整的系统。这里,我们使用 busybox 制作一个最小根文件系统,加深我们对根文件系统的理解。

0x41 使用 busybox 制作根文件系统

1 下载和编译 busybox

busybox 官网 下载 busybox ,解压

tar -jxvf busybox-1.31.1.tar.bz2 
cd busybox-1.31.1/

方法一:静态编译

编译,busybox 编译的方式与内核编译类似,在配置文件中,建议选择静态编译

make distclean
make menuconfig ARCH=arm
# menuconfig begin
Settings -->
    [*] Build static binary (no shared libs)
# menuconfig end

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

方法二:动态链接

如果你嫌麻烦,可以直接使用默认配置,默认配置是使用动态链接的方式进行编译

make distclean
make defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- all install

busybox 默认安装到 ./_install 目录下

lys@kali:~/Documents/busybox-1.31.1/_install$ ls
bin  linuxrc  sbin  usr

2 创建 rootfs 目录

busybox 装完成后,会在 busybox 目录下生成 _install 目录,该目录下的程序就是单板运行所需要的命令。

拷贝该目录下的文件

rm linuxrc # 此文件可删除
cp -r * ~/Documents/rootfs/

3 复制动态链接库

从交叉编译器中拷贝所需要的动态链接库,复制到 lib 库目录下(如果 busybox 是静态编译的话,可以忽略此步骤

lys@kali:~/Documents/rootfs$ mkdir lib
lys@kali:~/Documents/rootfs$ sudo cp -r /usr/arm-linux-gnueabi/lib/* ./lib/

4 测试目标二进制程序是否能够运行

使用 qemu-user 模式测试 busybox 是否能够运行。新版 kali 没有内置 user 模式的 qemu ,需要我们下载安装之后,再进行测试

sudo apt-get install qemu-user

qemu-arm 测试 busybox
在这里插入图片描述
测试成功之后,需要新建终端,因为刚刚的 export QEMU 命令已经修改了系统的库目录路径,为了重新设置环境变量,需要新建终端,再进行后续的操作步骤

5 创建 4 个 tty 终端设备

mknod 用于创建 linux 中的字符设备和块设备,tty1 是设备的名字,c 是指块设备,4 是主设备号 /dev/devices 里面记录现有的设备,1 表示第一个子设备

$ mkdir ./dev
$ sudo mknod ./dev/tty1 c 4 1
$ sudo mknod ./dev/tty2 c 4 2
$ sudo mknod ./dev/tty3 c 4 3
$ sudo mknod ./dev/tty4 c 4 4

6 生成映象文件

lys@kali:~/Documents$ dd if=/dev/zero of=a9rootfs.ext3 bs=1M count=16

像 /dev/nul l一样,/dev/zero 也是一个伪文件,但它实际上产生连续不断的 null 的流(二进制的零流,而不是ASCII型的)。写入它的输出会丢失不见,/dev/zero 主要的用处是用来创建一个指定长度用于初始化的空文件,像临时交换文件。

格式化生成 ext3 文件系统

mkfs.ext3 a9rootfs.ext3

在这里插入图片描述

7 将 rootfs 挂载到制作的 ext3 文件系统映象

方法一:

sudo mkdir tmpfs

# -o loop=:使用 loop 模式用来将一个档案当成硬盘分割挂上系统。
sudo mount -t ext3 a9rootfs.ext3 tmpfs/ -o loop

sudo cp -r rootfs/* tmpfs/
sudo umount tmpfs

方法二:

sudo mount -t ext3 a9rootfs.ext3 rootfs/ -o loop
sudo umount rootfs

0x42 挂载根文件系统

在挂在根文件系统之前,先使用上一章的方法启动内核,看看内核中支持的块设备的名称
在这里插入图片描述
以 versatile 开发板为例,支持的设备以及文件系统如上所示,可以看到,只支持 ext2 ,而非我们刚刚制作的 ext3,重新制作个根文件系统就好了。或者使用 vexpress 开发板,这个开发板功能更为高级,支持挂载 ext3 文件系统。

sudo qemu-system-arm -M versatilepb -m 256M -kernel linux-5.6.6/arch/arm/boot/zImage -dtb linux-5.6.6/arch/arm/boot/dts/versatile-pb.dtb -append "root=/dev/ram0 ttyAMA0" -serial stdio -sd a9rootfs.ext2

root=/dev/.. 就是上图中,开发板硬件某个设备的名称。经过验证,verstaile 开发板较老,不能很好的支持我们制作的根文件系统,改换 vexpress,重新编译内核和文件系统,挂载成功。

qemu-system-arm -M vexpress-a9 -m 512M -kernel linux-5.6.6/arch/arm/boot/zImage \
-dtb linux-5.6.6/arch/arm/boot/dts/vexpress-v2p-ca9.dtb \
-append "root=/dev/mmcblk0 console=ttyAMA0" \
-sd a9rootfs.ext3 -serial stdio

系统成功启动
在这里插入图片描述

0x50 Q&A 可能遇到的问题

0x51 编译内核

fatal error: openssl/opensslv.h: No such file or directory
在这里插入图片描述
这是由于没有下载 openssl 库,下载安装即可

sudo apt-get install libssl-dev

/bin/sh: 1: bc: not found
在这里插入图片描述
这是由于缺少计算器程序 bc 造成的,下载安装

sudo apt-get install bc

No rule to make target ‘debian/certs/debian-uefi-certs.pem’
在这里插入图片描述
打开 .config 文件,注释下面这一句话

CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/[email protected]"

0x52 编译 busybox

Trying libraries: m resolv
在这里插入图片描述
这个其实代表编译成功,因为没有打印 make error

0x53 加载文件系统

error while loading shared libraries
在这里插入图片描述
在加载 init 文件时出错,此类错误多数是因为符号链接的问题,跟库有关系,建议重新编译 busybox,注意要用静态链接的方式编译。

Kernel panic - not syncing: No working init found
在这里插入图片描述
原因一:最小文件系统制作有问题,注意是否生成了目标平台的 busybox

原因二:应用程序通过eabi接口编译,内核需要支持这种接口。因此需要重新编译内核,在 Kernel Feture下选中 Use the ARM EABI to compile the kernel
在这里插入图片描述

0x60 总结

使用 qemu 模拟硬件设备,运行内核,并不是我们的最终目的,后续要做的是提取根文件系统,并将其挂在,这样就可以实现一个完整的设备模拟。

猜你喜欢

转载自blog.csdn.net/song_lee/article/details/105815237