目前多数linux开发者使用的开发环境是x86开发环境,有时项目出现问题时,软件运行环境不是x86环境,是嵌入式环境,如arm64等环境,这时候又没有嵌入式设备可供硬件调试,这时候就需要搭建虚拟嵌入式运行环境进行调试。本文主要介绍如何在ubuntu下搭建arm64调试环境。
sudo apt-get install -y binutils-aarch64-linux-gnusudo apt-get install -y gcc-aarch64-linux-gnu# 检查安装信息aarch64-linux-gnu-gcc -v# 安装libncurses-devsudo apt-get install -y libncurses-devsudo apt-get install -y flexsudo apt-get install -y bison
aarch64-linux-gnu,64-bit Armv8 Cortex-A, little-endian
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.xztar 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# 设置PATH环境变量,使系统优先找到下载的交叉编译器export PATH=$PWD/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin:$PATH
./configure --target-list=aarch64-softmmu,aarch64-linux-user --enable-virtfs --enable-debugmake -j4sudo make install
# 设置环境变量export ARCH=arm64export CROSS_COMPILE=aarch64-linux-gnu-# 配置编译选项make defconfigmake menuconfig# 编译make -j4
wget http://ftp.gnu.org/gnu/hello/hello-2.6.tar.gztar xzf hello-2.6.tar.gzcd hello-2.6./configure --host=aarch64-linux-gnumake
备注:执行./configure时可能出现错误,如下:
checking build system type... Invalid configuration `aarch64-linux': machine `aarch64' not recognized
解决办法,下载最新的配置文件,并替换:
wget -O config.guess 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD'wget -O config.sub 'http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD'find . -name "config.guess"find . -name "config.sub"mv config.guess ./build-auxmv config.sub ./build-aux
重新make
make 完成后生成的可执行程序是hello-2.6/src/hello
查看hello依赖的动态库:
aarch64-linux-gnu-readelf hello-2.6/src/hello -a |grep lib
输出结果如下:
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]0x0000000000000001 (NEEDED) Shared library: [libc.so.6]0x0000000000000001 (NEEDED) Shared library: [ld-linux-aarch64.so.1]000000415068 000b00000402 R_AARCH64_JUMP_SL 0000000000000000 __libc_start_main@GLIBC_2.17 + 011: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.17 (2)34: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch639: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch644: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6135: 0000000000403998 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini167: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_200: 0000000000403920 120 FUNC GLOBAL DEFAULT 13 __libc_csu_init0x0020: 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/
当我们在 Qemu 上运行起来自己编译的内核之后,需要使用 busybox 构建一个文件系统,将此文件系统挂载上去就可以使用 busybox 提供的各种命令了。
进入 busybox 解压目录,执行make menuconfig进行配置,设置以下选项:
备注: Cross Compiler prefix需要设置,不然启动后会报如下错误:
编译:
make -j4sudo make install
编译完成后在busybox的同级目录会生成一个rootfs目录
进入rootfs,执行以下命令:
sudo mkdir dev etc mnt libsudo mkdir -p etc/init.d
进入etc/init.d中创建文件rcS,在文件中加入以下内容:
mkdir -p /procmkdir -p /tmpmkdir -p /sysmkdir -p /mnt/bin/mount -amkdir -p /dev/ptsmount -t devpts devpts /dev/ptsecho /sbin/mdev > /proc/sys/kernel/hotplugmdev -s
修改rcS为可执行:
chmod 777 rcS
在etc/ 目录下新建一个inittab文件,加入以下内容:
::sysinit:/etc/init.d/rcS::respawn:-/bin/sh::askfirst:-/bin/sh::ctrlaltdel:/bin/umount -a -r
在dev目录下执行以下命令:
sudo mknod console c 5 1sudo mknod null c 1 3
将交叉编译的lib拷贝到rootfs/lib中:
cd rootfs/lib# 如果使用的是`gcc-aarch64-linux-gnu`,拷贝如下目录sudo cp /usr/aarch64-linux-gnu/lib/* .# 如果使用的是`gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu`,拷贝如下目录# 具体需要拷贝哪些库是根据编译的运行程序(此处是gnu hello)依赖决定的# 查看依赖参靠上面的介绍aarch64-linux-gnu-readelf hello-2.6/src/hello -a |grep lib# sudo cp -r ../../gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/lib/ld-* .# 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中:
sudo cp ../hello-2.6/src/hello ./usr/bin
在rootfs目录执行以下命令:
sudo find . | sudo cpio -o -H newc > ../linux-5.4/rootfs.cpiocd ../linux-5.4gzip -c rootfs.cpio > rootfs.cpio.gz
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 | 添加网络设备 |
启动虚拟机后,执行如下命令,运行hello:
cd /usr/bin./hello
输出结果如下,表示环境搭建成功:
Hello, world!
编译并安装:
./configure --target=aarch64-linux-gnu --program-prefix=aarch64-linux- --prefix=/usr/local/aarch64_gdbmake && sudo make install
装完后将/usr/local/aarch64_gdb下的文件拷贝到rootfs下,然后重新制作根文件系统即可。
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 的。
qemu-system-aarch64 -kernel build/arch/arm64/boot/Image -append "console=ttyAMA0" -m 2048M -smp 4 -M virt -cpu cortex-a57 -nographic
qemu-system-aarch64 \-kernel build/arch/arm64/boot/Image \-append "console=ttyAMA0" \-m 2048M -smp 4 \-M virt -cpu cortex-a57 \-nographic