用本机 clang 进行交叉编译

Posted on November 16, 2024

你在 A 平台上编译一份代码,编译出来的结果,在 A 平台无法运行,只能在 B 平台运行。这个就叫交叉编译。

通常使用交叉编译,是因为 B 平台太弱鸡,性能无法胜任编译工作。

因此大部分交叉编译,都是发生在 x86 上为 arm 编译。

为了进行交叉编译,你需要使用一种专门为交叉编译而开发的工具 —— 交叉编译器

在 Gentoo 上,交叉编译器可以使用工具 ”crossdev” 自动构建出来。

比如

crossdev -t aarch64-unknow-linux-gnu

最后,你会获得一系列 aarch64-unknow-linux-gnu- 打头的编译工具。其中 aarch64-unknow-linux-gnu-gcc 就是 C语言的交叉编译器。 aarch64-unknow-linux-gnu-g++ 就是 C++ 的交叉编译器。还有 aarch64-unknow-linux-gnu-ld 自然就是交叉连接器。

这些工具的文件名,你可以传给编译脚本,就实现了交叉编译。

如果是 CLANG , 情况发生了一些变化。

首先,你还是可以通过重新编译 clang , 设定 clang 的目标平台,配置 clang 的安装前缀,最终获得一系列 aarch64-unknow-linux-gnu-llvm 开头的 llvm 工具链。

比如 aarch64-unknow-linux-gnu-llvm-link, aarch64-unknow-linux-gnu-llvm-as . 以及 aarch64-unknow-linux-gnu-clang 和 aarch64-unknow-linux-gnu-clang++。

但是,我要说但是了。这套做法,其实完全没有必要。

因为 LLVM 的设计结构就决定了,你当前系统里安装的 clang 编译器,体内已经蕴含了全部受llvm支持的平台的机器代码生成能力。 而 gcc 的体内,一次只能含有一个平台的机器码生成能力,其他架构的机器码生成功能会通过条件编译排除。这是 gcc 需要专门构建交叉编译器的原因。

在任何平台上,你都可以使用 clang 生成 任意平台的代码。方法是传递 -target 参数。

比如

clang++ -target aarch64-unknow-linux-gnu main.cpp

不出任何意外的,意外发生了。

clang 能成功编译,但是在 最后 连接 的阶段报错了。会提示找不到 crtbeginS.o 之类的文件。

其实很好理解。 clang 虽然拥有生成任意平台的二进制的能力,但是,编译并不只是产生目标文件。更重要的是,还需要连接到运行时库。

那么,这个运行时库要怎么获取呢? 答案是: 下载 alarm 的 根目录 包。

比如下载 ArchLinuxARM-aarch64-latest.tar.gz

将 ArchLinuxARM-aarch64-latest.tar.gz 解压到 /usr/gnemul/qemu-aarch64/

sudo tar -xvf ArchLinuxARM-aarch64-latest.tar.gz -C /usr/gnemul/qemu-aarch64/

然后使用这个命令编译

clang++ -target aarch64-unknow-linux-gnu --sysroot=/usr/gnemul/qemu-aarch64/ main.cpp

恭喜你,这次成功编译了。(截至今日, archlinuxarm 上带的 STL 是个有 bug 的版本,导致它的头文件有错误。见 bug, 如果遇到了,请相信我,不是我教的方法的问题,是真的系统带的头文件有 bug。自己按bug汇报修下吧。)

对于支持将 “clang++ -target aarch64-unknow-linux-gnu –sysroot=/usr/gnemul/qemu-aarch64/” 作为编译器的 autotools 工具来说,这个教程已经结束了。

因为只要配置环境变量

export CC="clang -target aarch64-unknow-linux-gnu --sysroot=/usr/gnemul/qemu-aarch64/"
export CXX="clang++ -target aarch64-unknow-linux-gnu --sysroot=/usr/gnemul/qemu-aarch64/"

autotools 系列工具就能正常运行了。

但是,同样的方法在 cmake 上会失效。 因为 cmake 会把 “clang++ -target aarch64-unknow-linux-gnu –sysroot=/usr/gnemul/qemu-aarch64/” 作为一个整体去调用可执行文件。 当然,系统里并不存在一个名为 “clang++ -target aarch64-unknow-linux-gnu –sysroot=/usr/gnemul/qemu-aarch64/” 的可执行文件。。。。

对 cmake 来说,还得多做一个工作,就是建立一个 wrapper。

比如写一个 aarch64-unknow-linux-gnu-clang 的脚本,脚本里面这么写

#!/bin/bash
exec clang -target aarch64-unknow-linux-gnu --sysroot=/usr/gnemul/qemu-aarch64/ $*

还有写一个 aarch64-unknow-linux-gnu-clang++ 的脚本,脚本里面这么写

#!/bin/bash
exec clang++ -target aarch64-unknow-linux-gnu --sysroot=/usr/gnemul/qemu-aarch64/ $*

这样,就可以如传统的交叉编译器做法一样,让 cmake 使用 aarch64-unknow-linux-gnu-clang 和 aarch64-unknow-linux-gnu-clang++ 作为编译器进行交叉。

Comments