前言
随着不断地学习(败家),现在我手里积攒了许多不同体系结构的设备,比如 x86_64、ARM、aarch64 等等,也是花了我不少的钱💸。
平常我主要是在自己的 x86_64 电脑上写 C/C++ 代码,然后进行编译和测试,测试没问题之后,便会将程序下载到其它架构的机器上。但C/C++是"一次编写处处编译"的语言,不同架构的机器要对用对应架构的编译工具链来完成编译,如果你用系统自带的 gcc 编译你的代码,那么大概率编译出来的程序只能在当前架构的机器上运行(对我来说就是x86_64)。
为了让编译出来的程序能在其他架构的机器上跑起来,得用对应架构的交叉编译工具链,或者干脆直接把代码丢到对应架构的机器上,它自己编译出来的程序自己大概率能用。
交叉编译,简单说就是在一种架构的机器上编译出给另一种架构用的程序。
这篇文章就用来记录我折腾过程中发现的一些交叉编译方法,目前摸索到的有以下两种:
- 交叉编译工具链
- Docker运行非本机架构编译(说实话这种方式严格来讲并非交叉编译的范畴,不过也能实现我们的目标)
本文的情景设定
假设你有一个朋友叫李华,你和他生活在一个只有 Linux 系统的世界。
某天,李华的C语言课程老师要求李华写一个经典的 hello world 程序作为第一节课的课后作业。
于是李华照着PTT创建了 ~/hello.c ,并往其中写入了如下的内容:
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
并使用 gcc 进行编译,得到了正确输出 hello world 的可执行文件 hello,如下:
~$ gcc --static -o hello hello.c
~$ ./hello
hello world
完全没问题!于是李华按照要求,把可执行文件 hello 发到了老师的邮箱(或者QQ、微信等平台)。
过了一会儿,李华的老师给他发了一张截图,说有BUG(BIG),他点开来一看,发现了如下的报错文本:
~/作业/李华boy$ ./hello
可执行文件格式错误: ./hello
# 当然也可能是找不到动态库的报错,反正李华老师的电脑大概率运行不了
然后李华就傻了,这是为啥啊?我电脑上运行的好好的。
原来老师的电脑是 aarch64 架构的,和李华的 x86_64 架构不同,李华用自带的 gcc 进行编译后,编译出来的可执行文件格式不对。
请你帮助李华正确的完成编译,将格式正确的可执行文件交给他。
交叉编译工具链
这个是最常见的一种办法,你可以下载对应架构对应的交叉编译工具链,用这个交叉编译工具链里面的gcc来编译程序。
通过情景设定可知李华老师的电脑架构为 aarch64,操作系统为 Linux,那么只要下载 aarch64-linux 交叉编译工具链就行了!
对应的工具链可以从 Arm GNU Toolchain Downloads 网站之中获取,如下图:
工具链下载演示
下载图中所示的 arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz 解压并放到对应的目录下,具体命令如下:
~$ wget https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
~$ tar -Jxvf arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu.tar.xz
~$ mkdir -p /usr/local/aarch64
~$ sudo mv arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu /usr/local/aarch64/
~$ echo 'export PATH=$PATH:/usr/local/aarch64/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-linux-gnu/bin' >> ~/.bashrc
上面的演示先是用 wget 下载了压缩包(当然,你可以直接在浏览器中下载),然后解压并将解压出来的文件全部移动到 /usr/local/aarch64/ ,最后在 bash shell 的初始化文件中给 PATH 环境变量添加对应的路径使Linux能直接找到我们接下来要用到的工具(如果你用的不是 bash ,比如 zsh,请自行改为放到对应的 shell 初始化文件中)。
最后你便可以使用交叉编译工具对李华的代码进行编译,得到能在李华老师电脑上运行的程序了,如下:
~$ aarch64-none-linux-gnu-gcc --static -o hello_aarch64 hello.c
这里得到的 hello_aarch64 在李华老师的电脑上便能正常执行,完成任务!
( 这里使用 --static 是因为李华和他老师电脑上的 glibc 版本可能不同 )
Docker运行非本机架构编译
说实话这种方式严格来讲并非交叉编译的范畴,不过也能实现我们的目标。
这个方法本质上就是根据对应架构和系统选择相应的Docker镜像创建容器,然后进入到容器之中进行编译。
通过情景设定可知李华老师的电脑架构为 aarch64,操作系统为 Linux,假设李华老师的电脑是 Ubuntu 24.04 ,我们按照接下来的方法即可得到我们想要的可执行程序。
~$ mkdir -p ./dir && mv helle.c ./dir/
# 安装所有架构的模拟器
~$ sudo docker run --privileged --rm tonistiigi/binfmt --install all
# 创建一次性容器
~$ sudo docker run -it --rm --platform linux/arm64 -v ./dir:/app ubuntu:20.04
# 编译
/# cd app
/app# apt update && apt install build-essential
/app# gcc -o hello hello.c
/app# exit
# 此时进入 dir 文件,即可获取对应架构的可执行程序
~$ cd dir
~/dir$ ls hello
hello
参考
开源项目
Arm GNU Toolchain Downloads
tonistiigi/binfmt
自媒体
让 Docker 运行非本机架构的镜像(binfmt_misc)
交叉编译?狗都不用,还是玩docker容器吧