Ubuntu下使用QEMU搭建ARM64开发调试环境
发布时间:2020-01-17 04:16:20.609 文章来源:AiSoftCloud 浏览次数:4034 下载次数:1 

简介

目前多数linux开发者使用的开发环境是x86开发环境,有时项目出现问题时,软件运行环境不是x86环境,是嵌入式环境,如arm64等环境,这时候又没有嵌入式设备可供硬件调试,这时候就需要搭建虚拟嵌入式运行环境进行调试。本文主要介绍如何在ubuntu下搭建arm64调试环境。

源码下载

下载Linux内核源码

下载busybox

下载QEMU

安装

安装交叉编译工具

gcc-aarch64-linux-gnu

  1. sudo apt-get install -y binutils-aarch64-linux-gnu
  2. sudo apt-get install -y gcc-aarch64-linux-gnu
  3. # 检查安装信息
  4. aarch64-linux-gnu-gcc -v
  5. # 安装libncurses-dev
  6. sudo apt-get install -y libncurses-dev
  7. sudo apt-get install -y flex
  8. sudo apt-get install -y bison

gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu

  1. wget https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
  2. tar xvf https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz
  3. # 设置PATH环境变量,使系统优先找到下载的交叉编译器
  4. export PATH=$PWD/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH

安装QEMU

  1. ./configure --target-list=aarch64-softmmu,aarch64-linux-user --enable-virtfs --enable-debug
  2. make -j4
  3. sudo make install

编译 Linux 内核

  1. # 设置环境变量
  2. export ARCH=arm64
  3. export CROSS_COMPILE=aarch64-linux-gnu-
  4. # 配置编译选项
  5. make defconfig
  6. make menuconfig
  7. # 编译
  8. make -j4

编译测试程序 GNU HELLO

  1. wget http://ftp.gnu.org/gnu/hello/hello-2.6.tar.gz
  2. tar xzf hello-2.6.tar.gz
  3. cd hello-2.6
  4. ./configure --host=aarch64-linux-gnu
  5. make

备注:执行./configure时可能出现错误,如下:

  1. checking build system type... Invalid configuration `aarch64-linux': machine `aarch64' not recognized

解决办法,下载最新的配置文件,并替换:

  1. wget -O config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'
  2. wget -O config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'
  3. find . -name "config.guess"
  4. find . -name "config.sub"
  5. mv config.guess ./build-aux
  6. mv config.sub ./build-aux

重新make

make 完成后生成的可执行程序是hello-2.6/src/hello

查看hello依赖的动态库:

  1. aarch64-linux-gnu-readelf hello-2.6/src/hello -a |grep lib

输出结果如下:

  1. [Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
  2. 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
  3. 0x0000000000000001 (NEEDED) Shared library: [ld-linux-aarch64.so.1]
  4. 000000415068 000b00000402 R_AARCH64_JUMP_SL 0000000000000000 __libc_start_main@GLIBC_2.17 + 0
  5. 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.17 (2)
  6. 34: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
  7. 39: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
  8. 44: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
  9. 135: 0000000000403998 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
  10. 167: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
  11. 200: 0000000000403920 120 FUNC GLOBAL DEFAULT 13 __libc_csu_init
  12. 0x0020: Version: 1 File: libc.so.6 Cnt: 1

下面在制作根文件系统时要将依赖的交叉编译的库文件ld-linux-aarch64.so.1 libc.so.6 ld-linux-aarch64.so.1拷贝到rootfs/lib中,或者将交叉编译工具所有的库文件拷贝进去即可,目录是/usr/aarch64-linux-gnu/lib/

使用busybox构建根文件系统

当我们在 Qemu 上运行起来自己编译的内核之后,需要使用 busybox 构建一个文件系统,将此文件系统挂载上去就可以使用 busybox 提供的各种命令了。

进入 busybox 解压目录,执行make menuconfig进行配置,设置以下选项:

  • Busybox Setting -> Build Options -> Build BusyBox as a static binary (no shared libs) (enable)
  • Networking Utilities -> inetd(disable)
  • Busybox Setting -> Installation Options (“make install” behavior) -> (../rootfs) BusyBox installation prefix
  • Busybox Setting -> Build Options -> (aarch64-linux-gnu-) Cross Compiler prefix

备注: Cross Compiler prefix需要设置,不然启动后会报如下错误:

  • request_module: kmod_concurrent_max (0) close to 0 (max_modprobes: 50), for module binfmt-464c, throttling…
  • request_module: modprobe binfmt-464c cannot be processed, kmod busy with 50 threads for more than 5 seconds now

编译:

  1. make -j4
  2. sudo make install

编译完成后在busybox的同级目录会生成一个rootfs目录

制作文件系统

进入rootfs,执行以下命令:

  1. sudo mkdir dev etc mnt lib
  2. sudo mkdir -p etc/init.d

进入etc/init.d中创建文件rcS,在文件中加入以下内容:

  1. mkdir -p /proc
  2. mkdir -p /tmp
  3. mkdir -p /sys
  4. mkdir -p /mnt
  5. /bin/mount -a
  6. mkdir -p /dev/pts
  7. mount -t devpts devpts /dev/pts
  8. echo /sbin/mdev > /proc/sys/kernel/hotplug
  9. mdev -s

修改rcS为可执行:

  1. chmod 777 rcS

在etc/ 目录下新建一个inittab文件,加入以下内容:

  1. ::sysinit:/etc/init.d/rcS
  2. ::respawn:-/bin/sh
  3. ::askfirst:-/bin/sh
  4. ::ctrlaltdel:/bin/umount -a -r

在dev目录下执行以下命令:

  1. sudo mknod console c 5 1
  2. sudo mknod null c 1 3

将交叉编译的lib拷贝到rootfs/lib中:

  1. cd rootfs/lib
  2. # 如果使用的是`gcc-aarch64-linux-gnu`,拷贝如下目录
  3. sudo cp /usr/aarch64-linux-gnu/lib/* .
  4. # 如果使用的是`gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu`,拷贝如下目录
  5. # 具体需要拷贝哪些库是根据编译的运行程序(此处是gnu hello)依赖决定的
  6. # 查看依赖参靠上面的介绍aarch64-linux-gnu-readelf hello-2.6/src/hello -a |grep lib
  7. # sudo cp -r ../../gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/lib/ld-* .
  8. # sudo cp -r ../../gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/lib/libc* .

将交叉编译的可执行程序hello拷贝到rootfs/usr/bin中:

  1. sudo cp ../hello-2.6/src/hello ./usr/bin

在rootfs目录执行以下命令:

  1. sudo find . | sudo cpio -o -H newc > ../linux-5.4/rootfs.cpio
  2. cd ../linux-5.4
  3. gzip -c rootfs.cpio > rootfs.cpio.gz

启动虚拟机

  1. qemu-system-aarch64 -cpu cortex-a57 -machine type=virt -nographic -smp 1 -m 4096 -kernel arch/arm64/boot/Image -append "rdinit=/linuxrc console=ttyAMA0" -initrd rootfs.cpio.gz -device virtio-scsi-device
qemu-system-aarch64 二进制文件,提供模拟aarch64架构的虚拟机进程
-m 2048 分配2048MB内存
-M virt 模拟成什么服务器,我们一般选择virt就可以了,他会自动选择最高版本的virt
-cpu cortex-a72 模拟成什么CPU,其中cortex-a53\a57\a72都是ARMv8指令集的
-smp 2,cores=2,threads=1,sockets=1 2个vCPU,这2个vCPU由qemu模拟出的一个插槽(socket)中的2个核心,每个核心支持一个超线程构成。
-bios xxx 指定bios bin所在的路径
-device xxx 添加一个设备,参数可重复
-drive 添加一个驱动器,参数可重复
-net 添加网络设备
  • 如果出现如下问题,可能设置的内存太小,加大内存即可(-m 参数):
  • end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

运行Hello

启动虚拟机后,执行如下命令,运行hello:

  1. cd /usr/bin
  2. ./hello

输出结果如下,表示环境搭建成功:

  1. Hello, world!

GDB调试

编译并安装:

  1. ./configure --target=aarch64-linux-gnu --program-prefix=aarch64-linux- --prefix=/usr/local/aarch64_gdb
  2. make && sudo make install

装完后将/usr/local/aarch64_gdb下的文件拷贝到rootfs下,然后重新制作根文件系统即可。

扩展

Linux启动过程

Linux kernel在自身初始化完成之后,需要能够找到并运行第一个用户程序(这个程序通常叫做“init”程序)。用户程序存在于文件系统之中,因此,内核必须找到并挂载一个文件系统才可以成功完成系统的引导过程。在grub中提供了一个选项“root=”用来指定第一个文件系统,但随着硬件的发展,很多情况下这个文件系统也许是存放在USB设备,SCSI设备等等多种多样的设备之上,如果需要正确引导,USB或者SCSI驱动模块首先需要运行起来,可是不巧的是,这些驱动程序也是存放在文件系统里,因此会形成一个悖论。

为解决此问题,Linux kernel 提出了一个RAM disk的解决方案,把一些启动所必须的用户程序和驱动模块放在RAM disk中,这个RAM disk看上去和普通的disk一样,有文件系统,有cache,内核启动时,首先把RAM disk挂载起来,等到init程序和一些必要模块运行起来之后,再切到真正的文件系统之中。

上面提到的RAM disk的方案实际上就是 initrd。 如果仔细考虑一下,initrd 虽然解决了问题但并不完美。 比如,disk 有cache 机制,对于 RAM disk 来说,这个cache机制就显得很多余且浪费空间;disk 需要文件系统,那文件系统(如ext2等)必须被编译进kernel而不能作为模块来使用。

Linux 2.6 kernel 提出了一种新的实现机制,即 initramfs。顾名思义,initramfs 只是一种 RAM filesystem 而不是 disk。initramfs 实际是一个 cpio 归档,启动所需的用户程序和驱动模块被归档成一个文件。因此,不需要 cache,也不需要文件系统。

QEMU has a command argument called “-kernel”. It is a very handy function!! Because of this feature, we don’t need to bother the complicated boot sequence and problems on locating Kernel Image. QEMU will uncompress the kernel image to a proper memory location and start to run the kernel code.

很显然指定-kernel /path/to/kernel_image即可。但是这样是无法正常启动 Linux 的。

  1. qemu-system-aarch64 -kernel build/arch/arm64/boot/Image -append "console=ttyAMA0" -m 2048M -smp 4 -M virt -cpu cortex-a57 -nographic
  1. qemu-system-aarch64 \
  2. -kernel build/arch/arm64/boot/Image \
  3. -append "console=ttyAMA0" \
  4. -m 2048M -smp 4 \
  5. -M virt -cpu cortex-a57 \
  6. -nographic
  • -m 指定内存大小
  • -M 指定虚拟机器「machine」的类型
  • -cpu 指定虚拟CPU的型号
  • -smp 指定对称多处理的核心数
  • -append 指定内核启动时使用的命令行参数「cmdline」

参考资料

更多文章可关注公众号
aisoftcloud