【Rust】交叉编译

交叉编译就是跨平台编译,例如在 window 下编译程序的linux版本,或者在 x86_64 平台下编译 aarch64 版本。跨平台编译在Go语言中非常方便,得益于Go语言汇编器的设计。

MacOS 交叉编译 Linux 程序

本文展示如何在 Apple M1 的平台下编译 Linux aarch64 的应用程序。

  • Apple M1

    1
    2
    3
    4
    5
    6
    7
    ~/WORKDIR/gamelife1314.github.io on  source! ⌚ 13:15:25
    $ uname -pvm
    Darwin Kernel Version 21.4.0: Fri Mar 18 00:46:32 PDT 2022; root:xnu-8020.101.4~15/RELEASE_ARM64_T6000 arm64 arm

    ~/WORKDIR/gamelife1314.github.io on  source! ⌚ 13:15:38
    $ gcc -dumpmachine
    arm64-apple-darwin21.4.0
  • linux aarch64

    1
    2
    3
    4
    5
    ubuntu@vm-docker:~$ gcc -dumpmachine
    aarch64-linux-gnu
    ubuntu@vm-docker:~$ uname -pvm
    #40-Ubuntu SMP Mon Mar 7 08:06:10 UTC 2022 aarch64 aarch64
    ubuntu@vm-docker:~$

安装 rust 标准库

在编译之前,我们需要在我们的工作平台,即 Mac 机器中,安装目的平台的标准库,我们可以通过 rustup target list 命令查看 rust 支持哪些平台架构,例如:

1
2
3
4
5
~/WORKDIR/rust/helloworld ⌚ 12:52:32
$ rustup target list | grep aarch64 | grep linux
aarch64-linux-android
aarch64-unknown-linux-gnu (installed)
aarch64-unknown-linux-musl

安装特定平台标准库使用 rustup target add 命令即可:

1
2
3
4
5
~/WORKDIR/rust/helloworld ⌚ 13:21:34
$ rustup target add --toolchain stable aarch64-unknown-linux-gnu
info: downloading component 'rust-std' for 'aarch64-unknown-linux-gnu'
info: installing component 'rust-std' for 'aarch64-unknown-linux-gnu'
34.9 MiB / 34.9 MiB (100 %) 18.1 MiB/s in 1s ETA: 0s

安装成功之后,通过 rustup show 命令查看本地已安装的工具链和标准库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
~/WORKDIR/rust/helloworld ⌚ 13:22:11
$ rustup show
Default host: aarch64-apple-darwin
rustup home: /Users/fudenglong/.rustup

installed toolchains
--------------------

stable-aarch64-apple-darwin
beta-aarch64-apple-darwin
nightly-aarch64-apple-darwin (default)

installed targets for active toolchain
--------------------------------------

aarch64-apple-darwin
aarch64-unknown-linux-gnu
x86_64-unknown-linux-gnu

active toolchain
----------------

nightly-aarch64-apple-darwin (default)
rustc 1.62.0-nightly (e745b4ddb 2022-04-07)

安装编译器

要编译 aarch64-linux-gnu 平台下的程序,就需要特定平台的编译器,这个项目中提供了MAC环境下用于交叉编译的工具,更多请看 https://github.com/messense/homebrew-macos-cross-toolchains

使用 brew 安装非常简单,两条命令即可,耐心等待安装完成:

1
2
brew tap messense/macos-cross-toolchains
brew install aarch64-unknown-linux-gnu

经过这里之后,我们可以看到我们将所需要的 linux-aarch64 下的工具链都安装下来了:

交叉编译

使用 cargo 命令快速创建一个项目进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
~/WORKDIR/rust ⌚ 13:27:16
$ cargo new crosscompile
Created binary (application) `crosscompile` package

~/WORKDIR/rust ⌚ 13:27:50
$ cd crosscompile

~/WORKDIR/rust/crosscompile on  master! ⌚ 13:27:58
$ cargo run
Compiling crosscompile v0.1.0 (/Users/fudenglong/WORKDIR/rust/crosscompile)
Finished dev [unoptimized + debuginfo] target(s) in 0.59s
Running `target/debug/crosscompile`
Hello, world!
配置文件配置 Cargo

我们需要配置 cargo 编译时使用的链接器,在配置文件 .cargo/config 中添加如下配置:

1
2
3
4
5
6
7
~/WORKDIR/rust/crosscompile on  master! ⌚ 17:15:21
$ ll .cargo/config
-rw-r--r-- 1 fudenglong staff 94B 4 8 17:13 .cargo/config
~/WORKDIR/rust/crosscompile on  master! ⌚ 17:15:25
$ cat .cargo/config
[target.aarch64-unknown-linux-gnu]
linker = "/opt/homebrew/bin/aarch64-unknown-linux-gnu-gcc"

然后我们执行特定编译命令 cargo build -v --target aarch64-unknown-linux-gnu

环境变量配置 Cargo

我们也可以使用环境变量配置所需要的链接器,环境变量的形式是:CARGO_TARGET_<triple>_LINKER,这里的 triple 就是我们目标 target,只不过要转换成大写,并且替换 -_,例如:

CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/opt/homebrew/bin/aarch64-unknown-linux-gnu-gcc cargo build -v --target aarch64-unknown-linux-gnu

编译输出结果

在下面的目录下能找到编译之后的二进制文件:

1
2
3
4
5
6
7
8
9
10
~/WORKDIR/rust/crosscompile on  master! ⌚ 17:22:58
$ ll target/aarch64-unknown-linux-gnu/debug
total 7880
drwxr-xr-x 2 fudenglong staff 64B 4 8 17:13 build
-rwxr-xr-x 1 fudenglong staff 3.8M 4 8 17:13 crosscompile
-rw-r--r-- 1 fudenglong staff 153B 4 8 17:13 crosscompile.d
drwxr-xr-x 4 fudenglong staff 128B 4 8 17:13 deps
drwxr-xr-x 2 fudenglong staff 64B 4 8 17:13 examples
drwxr-xr-x 3 fudenglong staff 96B 4 8 17:13 incremental

然后去linux下运行也是没有问题的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ubuntu@vm-docker:~/workdir$ uname
Linux
ubuntu@vm-docker:~/workdir$ uname -a
Linux vm-docker 5.13.0-35-generic #40-Ubuntu SMP Mon Mar 7 08:06:10 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux
ubuntu@vm-docker:~/workdir$ readelf -h crosscompile
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: AArch64
Version: 0x1
Entry point address: 0x6720
Start of program headers: 64 (bytes into file)
Start of section headers: 4025296 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 10
Size of section headers: 64 (bytes)
Number of section headers: 44
Section header string table index: 43
ubuntu@vm-docker:~/workdir$ ./crosscompile
Hello, world!
ubuntu@vm-docker:~/workdir$

其他场景

交叉编译的直接需求就是你得找到一款合适的编译器,能编译出特定平台架构下的二进制程序。

  1. macos->windowsMacos 平台linux和windows交叉编译器
  2. linuxARM compiler for LinuxX86 System

参考文章

  1. Rust Cross-Compilation with Cargo and Rustup toolchain manager
  2. 如何在Mac上为Linux交叉编译Rust程序
  3. Rust交叉编译Mac编译Linux/Windows平台
  4. Macos 平台linux和windows交叉编译器