之前编写过一个小框架,最近需要为它编译一个arm64的产出,但是由于arm板子不在手上,只能在x86上编译出arm的产出后再发给对方部署。
本文就以此场景为例,讲解如何通过qemu、gcc交叉编译等方法,在x86机器上搭建arm64的编译环境并完成arm编译。
测试环境信息和版本如下:
- x86平台:x86 ubuntu22.04
- arm平台:arm64 ubuntu20.04
- arm gcc version :9.5.0
- qemu版本:6.2.0
- 交叉编译工具链:aarch64-linux-gnu
一、安装qemu
我们可以下载QEMU的源码通过编译的方式安装,也可以直接apt方式安装:
# apt安装
sudo apt install -y qemu qemu-system qemu-user samba # 安装指定平台 qemu-system-aarch64
or
# 源码安装
sudo apt install -y ninja-build libpixman-1-dev libcap-ng-dev libattr1-dev
wget https://download.qemu.org/qemu-6.2.0.tar.xz
tar xvJf qemu-6.2.0.tar.xz
cd qemu-6.2.0
./configure --enable-kvm --enable-virtfs # 安装指定平台 --target-list=aarch64-softmmu
make && make install
查看版本号:
$ qemu-system-aarch64 --version
QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.8)
Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
二、构建arm系统
# 下载ubuntu20.04 arm镜像
mkdir qemu
wget http://cdimage.ubuntu.com/ubuntu-legacy-server/releases/20.04/release/ubuntu-20.04.1-legacy-server-arm64.iso
# 下载uefi驱动
wget http://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd
# 格式化出一个系统盘
qemu-img create -f raw ubuntu20.04.4-arm64.img 30g
# 安装arm虚拟系统(需要ssh登录的话,记得最后步骤选装下sshd server)
qemu-system-aarch64 -m 8096 -cpu cortex-a57 -smp 8 -M virt -bios QEMU_EFI.fd -nographic -drive if=none,file=ubuntu-20.04.1-legacy-server-arm64.iso,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom -drive if=none,file=ubuntu20.04.4-arm64.img,id=hd0 -device virtio-blk-device,drive=hd0
qemu-img info ubuntu20.04.4-arm64.img
# 关闭后重新启动并进入系统
qemu-system-aarch64 -m 8096 -cpu cortex-a57 -smp 8 -M virt -bios QEMU_EFI.fd -nographic -drive if=none,format=raw,file=ubuntu20.04.4-arm64.img,id=hd0 -device virtio-blk-device,drive=hd0 -netdev user,hostfwd=tcp::2222-:22,id=netdev0, -device e1000,netdev=netdev0
# 参数:9p协议挂载磁盘
-fsdev local,security_model=passthrough,id=fsdev0,path=/home/work -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=qemu_share
sudo mount -t 9p -o trans=virtio,version=9p2000.L qemu_share /mnt
# 参数:smb协议挂载磁盘
-net nic -net user,smb=/home/work
apt update && apt install cifs-utils # arm虚拟机运行
sudo mount -t cifs //10.0.2.4/qemu/ /mnt # 别修改ip,原样执行即可把smb参数的路径挂载到虚拟机的/mnt目录(注意这里要输入两次密码,一次是sudo需要的当前登陆密码,一次是mount需要的root密码;ubuntu系统的默认密码为ubuntu)
#sudo umount -a -t cifs -l #卸载挂载
# 镜像格式转换(qcow2格式不占空间但性能较低,磁盘大的话建议raw)
#qemu-img convert -f raw ubuntu20.04.4-arm64.img -O qcow2 ubuntu20.04.4-arm64.qcow2.img
qemu-system-aarch64命令参数解释如下:
-m 内存大小(这里为8G)
-cpu cpu型号(最新的a78e型号模拟不了)
-smp 核数目(最大模拟8核)
-nographic 不使用图形界面
-drive 驱动器映像文件
-device 设备
-netdev 网络设备(hostfwd:端口映射;smb磁盘挂载)
-fsdev 使用9p virtio支持virtfs共享,其中path参数为Host主机中共享目录的路径
三、arm编译
# 通过ssh进入系统
ssh work@127.0.0.1 -p 2222
# 安装编译依赖
sudo apt install -y cmake build-essential net-tools libprotobuf-dev protobuf-compiler libjsoncpp-dev libzmq3-dev libgtest-dev libgflags-dev libglog-dev
# 编译
cd project/xxx
cmake ..
make
make install
四、kvm加速
裸使用qemu明显感觉到速度有点慢,这是因为qemu自己用软件模拟了整套的虚拟机实现,包括处理器虚拟化、内存虚拟化以及I/O设备的虚拟化等,而linux内核有很好用的kvm来硬件模拟CPU和内存,性能要更好些,所以如果你的宿主机支持kvm,我们可以使用它来代替qemu的cpu/mem模拟功能,以提升虚拟机的性能。
所以简单直接的理解就是:QEMU是个计算机模拟器,而KVM为计算机的模拟提供加速功能。
检查宿主机是否支持kvm:
$ grep -E 'vmx|svm' /proc/cpuinfo #如果有输出则表示硬件有虚拟化支持
$ lsmod | grep kvm # 如果kvm_intel/kvm_amd、kvm模块被显示出来,则kvm模块已经加载
kvm_intel 142999 0
kvm 444314 1 kvm_intel
创建镜像时指定参数:
qemu-img create -f raw ubuntu20.04.4-arm64.kvm.img 30g
qemu-system-aarch64 -enable-kvm -m 8096 -cpu cortex-a57 -smp 8 -M virt -bios QEMU_EFI.fd -nographic -drive if=none,file=ubuntu-20.04.1-legacy-server-arm64.iso,id=cdrom,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom -drive if=none,file=ubuntu20.04.4-arm64.kvm.img,id=hd0 -device virtio-blk-device,drive=hd0 -fsdev local,security_model=passthrough,id=fsdev0,path=/home/work -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=qemu_share
其他同上。
五、使用gcc-aarch64交叉编译
除了使用qemu以外,我们也可以在x86系统里安装arrch64版本的gcc,并在x86下编译出arm的产出。
可以通过命令”apt-cache search aarch64″ 查看系统源中有哪些安装包可供安装:
apt-cache search aarch64
... ...
gcc-9-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture)
gcc-10-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture)
gcc-11-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture)
gcc-12-aarch64-linux-gnu - GNU C compiler (cross compiler for arm64 architecture)
... ...
为了确保跟arm硬件上的gcc保持一直,这里选择”gcc-9-aarch64-linux-gnu”进行安装:
sudo apt-get install gcc-9-aarch64-linux-gnu g++-9-aarch64-linux-gnu # apt安装版本为gcc9.5+glibc2.34,需要与自己arm硬件上的glibc版本匹配
# 为方便使用可以软连接
sudo ln -s /usr/bin/aarch64-linux-gnu-gcc-9 /usr/bin/aarch64-linux-gnu-gcc
sudo ln -s /usr/bin/aarch64-linux-gnu-g++-9 /usr/bin/aarch64-linux-gnu-g++
/usr/bin/aarch64-linux-gnu-gcc --version
测试代码:
vim /home/work/test/test.c
#include <stdio.h>
int main(int argc, char **argv){
printf("build arm succ!\n");
return 0;
}
编译:
aarch64-linux-gnu-gcc test.c -o test
到arm对应挂载磁盘下运行:
work@qemu-ubuntu-arm:/mnt/test$ ./test
build arm succ!
使用cmake编译时的参数:
# arch
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=cortex-a72 -mcpu=cortex-a72")
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc CACHE FILEPATH "")
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++ CACHE FILEPATH "")
set(CMAKE_STRIP /usr/bin/aarch64-linux-gnu-strip CACHE FILEPATH "")
set(CMAKE_RANLIB /usr/bin/aarch64-linux-gnu-ranlib CACHE FILEPATH "")
set(CMAKE_AR /usr/bin/aarch64-linux-gnu-ar CACHE FILEPATH "")
include_directories(
/usr/aarch64-linux-gnu/include/
)
link_directories(
/usr/aarch64-linux-gnu/lib/
)
五、总结
至此,我们尝试了通过qemu搭建虚拟arm系统,也尝试了直接在x86下交叉编译arm产出。如果需要编译其他操作系统的话也可以采用本文的方法,只需要替换其中的操作系统类型即可。
yan 23.6.1
参考: