目前多数linux开发者使用的开发环境是x86开发环境,有时项目出现问题时,软件运行环境不是x86环境,是嵌入式环境,如arm64等环境,这时候又没有嵌入式设备可供硬件调试,这时候就需要搭建虚拟嵌入式运行环境进行调试。本文主要介绍如何在ubuntu下搭建arm64调试环境。
sudo apt-get install -y binutils-aarch64-linux-gnu
sudo apt-get install -y gcc-aarch64-linux-gnu
# 检查安装信息
aarch64-linux-gnu-gcc -v
# 安装libncurses-dev
sudo apt-get install -y libncurses-dev
sudo apt-get install -y flex
sudo 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.xz
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
# 设置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-debug
make -j4
sudo make install
# 设置环境变量
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
# 配置编译选项
make defconfig
make menuconfig
# 编译
make -j4
wget http://ftp.gnu.org/gnu/hello/hello-2.6.tar.gz
tar xzf hello-2.6.tar.gz
cd hello-2.6
./configure --host=aarch64-linux-gnu
make
备注:执行./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-aux
mv 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 + 0
11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.17 (2)
34: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
39: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
44: 0000000000000000 0 FILE LOCAL DEFAULT ABS /usr/lib/gcc-cross/aarch6
135: 0000000000403998 4 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
167: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
200: 0000000000403920 120 FUNC GLOBAL DEFAULT 13 __libc_csu_init
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/
当我们在 Qemu 上运行起来自己编译的内核之后,需要使用 busybox 构建一个文件系统,将此文件系统挂载上去就可以使用 busybox 提供的各种命令了。
进入 busybox 解压目录,执行make menuconfig
进行配置,设置以下选项:
备注: Cross Compiler prefix需要设置,不然启动后会报如下错误:
编译:
make -j4
sudo make install
编译完成后在busybox的同级目录会生成一个rootfs目录
进入rootfs,执行以下命令:
sudo mkdir dev etc mnt lib
sudo mkdir -p etc/init.d
进入etc/init.d中创建文件rcS,在文件中加入以下内容:
mkdir -p /proc
mkdir -p /tmp
mkdir -p /sys
mkdir -p /mnt
/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -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 1
sudo 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.cpio
cd ../linux-5.4
gzip -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_gdb
make && 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