Linux开发学习记录(4):如何愉快(?)地交叉编译

成元 2026-03-03 { 教程, 笔记 } [ Linux, C, C++, GCC, Docker ]

前言

随着不断地学习(败家),现在我手里积攒了许多不同体系结构的设备,比如 x86_64、ARM、aarch64 等等,也是花了我不少的钱💸。

平常我主要是在自己的 x86_64 电脑上写 C/C++ 代码,然后进行编译和测试,测试没问题之后,便会将程序下载到其它架构的机器上。但C/C++是"一次编写处处编译"的语言,不同架构的机器要对用对应架构的编译工具链来完成编译,如果你用系统自带的 gcc 编译你的代码,那么大概率编译出来的程序只能在当前架构的机器上运行(对我来说就是x86_64)。

为了让编译出来的程序能在其他架构的机器上跑起来,得用对应架构的交叉编译工具链,或者干脆直接把代码丢到对应架构的机器上,它自己编译出来的程序自己大概率能用。

交叉编译,简单说就是在一种架构的机器上编译出给另一种架构用的程序。

这篇文章就用来记录我折腾过程中发现的一些交叉编译方法,目前摸索到的有以下两种:

本文的情景设定

假设你有一个朋友叫李华,你和他生活在一个只有 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容器吧