背景
在工作中我们经常需要交叉编译一些可执行程序或者动态库,有时要编译的程序过于复杂,如果靠纯的交叉编译,费事又费力,需要解决大量的编译依赖以及报错。
解决方案 docker + qemu-user
利用qemu-user可以运行不同架构的用户态程序,而docker可以创建一个运行不同架构的用户程序的环境。
原理
这里用到了linux提供的binfmt_misc,可以根据可执行程序的格式来调用不同的处理程序去打开。
参考:linux下使用binfmt_misc设定不同二进制的打开程序
# mount -t binfmt_misc binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,nosuid,nodev,noexec,relatime) # ls /proc/sys/fs/binfmt_misc/ llvm-10-runtime.binfmt qemu-aarch64 qemu-cris qemu-mips qemu-mipsn32 qemu-ppc64abi32 qemu-s390x qemu-sparc32plus register llvm-12-runtime.binfmt qemu-alpha qemu-hppa qemu-mips64 qemu-mipsn32el qemu-ppc64le qemu-sh4 qemu-sparc64 status python2.7 qemu-arm qemu-m68k qemu-mips64el qemu-ppc qemu-riscv32 qemu-sh4eb qemu-xtensa python3.8 qemu-armeb qemu-microblaze qemu-mipsel qemu-ppc64 qemu-riscv64 qemu-sparc qemu-xtensaeb # cat /proc/sys/fs/binfmt_misc/qemu-aarch64 enabled interpreter /usr/bin/qemu-aarch64-static flags: OCF offset 0 magic 7f454c460201010000000000000000000200b700 mask ffffffffffffff00fffffffffffffffffeffffff
步骤
下面以在CPU架构为x86_64上Host机器上编译一个目标架构为ARM64的可执行程序为例进行说明。
安装qemu-user
sudo apt install qemu-user-static
下载目标架构的docker镜像
以ubuntu为例,从这个链接中可以找到支持的不同架构的ubuntu镜像,或者从这里可以看到更多的信息。
安装
sudo docker pull arm64v8/ubuntu
运行容器
$ docker run -it -d -v /usr/bin/qemu-aarch64-static:/usr/bin/qemu-aarch64-static arm64v8/ubuntu:latest $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 206e91129cb4 arm64v8/ubuntu:latest "/bin/bash" 2 seconds ago Up 2 seconds eager_northcutt
由于容器运行在一个独立的mount命名空间,而binfmt_misc目录下的qemu-aarch64文件里设置的解析器的路径是:interpreter /usr/bin/qemu-aarch64-static,将来内核也会去同一个mount命名空间里去找/usr/bin/qemu-aarch64-static,因此必须将该解析器映射到容器里。
进入容器
docker exec -it 206e91129cb4 /bin/bash
安装必要的软件包
更新软件
root@206e91129cb4:/# apt update Get:1 http://ports.ubuntu.com/ubuntu-ports jammy InRelease [270 kB] Get:2 http://ports.ubuntu.com/ubuntu-ports jammy-updates InRelease [119 kB] Get:3 http://ports.ubuntu.com/ubuntu-ports jammy-backports InRelease [108 kB] Get:4 http://ports.ubuntu.com/ubuntu-ports jammy-security InRelease [110 kB] Get:5 http://ports.ubuntu.com/ubuntu-ports jammy/multiverse arm64 Packages [224 kB] Get:6 http://ports.ubuntu.com/ubuntu-ports jammy/universe arm64 Packages [17.2 MB] Get:7 http://ports.ubuntu.com/ubuntu-ports jammy/main arm64 Packages [1758 kB] Get:8 http://ports.ubuntu.com/ubuntu-ports jammy/restricted arm64 Packages [24.2 kB] Get:9 http://ports.ubuntu.com/ubuntu-ports jammy-updates/universe arm64 Packages [1096 kB] Get:10 http://ports.ubuntu.com/ubuntu-ports jammy-updates/restricted arm64 Packages [514 kB] Get:11 http://ports.ubuntu.com/ubuntu-ports jammy-updates/multiverse arm64 Packages [27.8 kB] Get:12 http://ports.ubuntu.com/ubuntu-ports jammy-updates/main arm64 Packages [973 kB] Get:13 http://ports.ubuntu.com/ubuntu-ports jammy-backports/universe arm64 Packages [23.6 kB] Get:14 http://ports.ubuntu.com/ubuntu-ports jammy-backports/main arm64 Packages [49.0 kB] Get:15 http://ports.ubuntu.com/ubuntu-ports jammy-security/main arm64 Packages [695 kB] Get:16 http://ports.ubuntu.com/ubuntu-ports jammy-security/multiverse arm64 Packages [23.4 kB] Get:17 http://ports.ubuntu.com/ubuntu-ports jammy-security/universe arm64 Packages [846 kB] Get:18 http://ports.ubuntu.com/ubuntu-ports jammy-security/restricted arm64 Packages [513 kB] Fetched 24.6 MB in 18s (1375 kB/s) Reading package lists... Done Building dependency tree... Done Reading state information... Done All packages are up to date.
安装编译器
root@206e91129cb4:/# apt install build-essential
编译的软件包
root@206e91129cb4:~# cd stressapptest-1.0.9 root@206e91129cb4:~/stressapptest-1.0.9# LDFLAGS=--static ./configure root@206e91129cb4:~/stressapptest-1.0.9# make root@206e91129cb4:~/stressapptest-1.0.9# file src/stressapptest src/stressapptest: ELF 64-bit LSB executable, ARM aarch64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=a2496cb7d8610d4bba6ae8744da921fea5b44c53, for GNU/Linux 3.7.0, not stripped
完。