交叉编译和静态链接 Rust 库

Photo by Zun Zun from Pexels

在 CSIS 中,我们过去使用 Python 编写后端,同时为 Incident Response Toolkit 编写一些 C/C++ 代码。

几年前,主要是由于性能原因,我们开始用 Rust 替换 Python 重写了一些特定的后端服务,并取得了巨大的成功。现在,为了便于开发和测试,我们正在探索将 C/C++ 代码库的某些部分也迁移到 Rust 的方法。

为了做到这一点,我们决定尝试将Rust集成到现有的代码库中,而不是一次重写所有内容。

下面是我们实验的摘要,以及编写一个Rust库并从 C/C++ 应用程序调用它的框架。

Targets

为了能够交叉编译代码,我们需要确保安装了相应的 target。可用的targets可以用过 rustc --print target-listrustup target list 查看,可以通过 rustup target add <target> 安装新的 target

查看已经安装的 targets 可以用 rustup show:

$ rustup show
Default host: x86_64-unknown-linux-gnu

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

stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu

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

i686-pc-windows-gnu
i686-pc-windows-msvc
i686-unknown-linux-gnu
i686-unknown-linux-musl
x86_64-pc-windows-gnu
x86_64-pc-windows-msvc
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl

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

stable-x86_64-unknown-linux-gnu (default)
rustc 1.31.0 (abe02cefd 2018-12-04)

项目设置

target 正确设置好后,我们需要设置项目,从 Cargo.toml 开始 :

[package]
name = "mylib"
version = "0.1.0"
authors = ["john doe<jdo@csis.dk>"]

[dependencies]
libc = "*"

[lib]
crate-type = ["staticlib"]

现在我们的库的源代码src/lib.rs :

extern crate libc;
use libc::uint32_t;

#[no_mangle]
pub extern "C" fn addition(a: uint32_t, b: uint32_t) -> uint32_t {
    a + b
}

以及应用程序调用者的源代码 caller.cpp:

#include <stdio.h>
#include <stdint.h>

extern "C" {
     uint32_t
     addition(uint32_t, uint32_t);
}

int main(void) {
    uint32_t sum = addition(1, 2);
    printf("%u\n", sum);
}

如果这是个 C 应用,只需要删除extern "C"

接下来,我们需要指定我们想要的 target, 这是在.cargo/config 中完成的, 比如:

[build]
target="i686-pc-windows-gnu"

Linux 二进制文件

为了静态链接 Linux 二进制文件,我们需要将 target 设置为 -linux-musl

  • .cargo/config

或者,对于 64 位 二进制文件,我们可以选择 x86_64-unknown-linux-musl

[build]
target="i686-unknown-linux-musl"
  • build
$ g++ -m32 -c -o linux_cpp_caller.o caller.cpp
$ g++ -static -m32 linux_cpp_caller.o -o linux_cpp_caller \
     -L./target/i686-unknown-linux-musl/debug/ -lmylib

如果我们的目标是 64 位,我们需要删除 -m32选项,并相应的调整 -L 标志。

Windows 二进制文件

为了静态链接 Windows 二进制文件,我们需要将 target 设置为 -pc-windows-gnu。对于 32 位二进制文件,需要特别注明异常处理模型。可以在 “rust-lang” Github repositorystack overflow 找到一个很好的摘要。

  • .cargo/config
[build]
target="i686-pc-windows-gnu"
rustflags = "-C panic=abort"

或者,对于 64 位二进制文件,我们可以选择 x86_64-pc-windows-gnu。如果是这种情况,可以删除 rustflags 选项。

  • build
$ i686-w64-mingw32-g++ -c -o win_cpp_caller.o caller.cpp
$ i686-w64-mingw32-g++ -static win_cpp_caller.o -o win_cpp_caller \
    -L./target/i686-pc-windows-gnu/debug/ \
    -lmylib \
    -ladvapi32 \
    -lws2_32 \
    -luserenv

如果是 64 位环境,用该使用 x86_64-w64-mingw32-g++,并相应调整 -L 标志。

结论

通过这篇文章,我们希望提供一个简单的工作示例,说明 Rust 和 C / C++ 如何相互操作,这可以作为一个起点。

我们要感谢以下链接资源的贡献者。

资源

这篇文章总结了以下链接中的信息:


本文翻译自 cross compiling and statically linking against Rust libraries

发表评论

电子邮件地址不会被公开。 必填项已用*标注

4 + 4 =