在macOS上交叉编译并链接带有货物和铁锈的动态库(cdylib)

时间:2019-08-26 10:00:14

标签: rust cross-compiling ffi musl

我有一个Rust库,其中公开了FFI的一些功能。我假设我必须将crate-type设置为cdylib-因为我想从Ruby和PHP调用这些函数(通过ffi ruby​​ gem来调用)。 但是我很难将其从OSX交叉编译到Linux。我尝试遵循一些使用musl libc的{​​{3}} -这是用于静态lib的,但是我什么也没找到。

因此,链接器的定义方式如下:

# .cargo/config
[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"

然后我尝试使用以下命令进行编译:

cargo build --release --target x86_64-unknown-linux-musl

但是有立即出现的错误:

error: cannot produce cdylib for `my-crate-name` as the target `x86_64-unknown-linux-musl` does not support these crate types

我的问题是:哪些目标/链接器对可以用于交叉编译cdylib?为什么Musl不支持这些箱子类型?甚至有可能吗?

1 个答案:

答案 0 :(得分:2)

问题

  

我有一个Rust库,其中公开了FFI的一些功能。因此,我必须将crate-type设置为cdylib

您从何处获得此信息?您可以创建动态库(.so)或静态库(.a)。 Alex的存储库中有很多示例:rust-ffi-examples

musl用于确实要创建二进制文件的情况,该二进制文件是静态链接的,完全没有依赖性(staticlib)。一切都在二进制文件中。您将其扔到Linux机器上,它将起作用。

动态链接用于以下情况:您知道将满足所有依赖性,并且希望使用较小的二进制文件,等等(cdylib)。但是,您必须确保依赖项确实存在,否则它将无法正常工作。

交叉编译

我通常不关心交叉编译,因为如果您需要动态链接到其他Linux库,这可能会非常棘手。对于这些情况,我有:

  • 在VMware Fusion中安装了Linux,
  • 安装了Linux映像的Mac版Docker。

有很多方法可以实现您想要的。参见@Shepmaster评论:使用CI并将构建工件上传到某个地方。

您真的需要交叉编译吗?还有其他方法可以实现您的目标吗?尽可能避免。

动态库

工具链

$ brew tap SergioBenitez/osxct
$ brew install x86_64-unknown-linux-gnu
$ rustup target add x86_64-unknown-linux-gnu

~/.cargo/config中添加以下行:

[target.x86_64-unknown-linux-gnu]
linker = "x86_64-unknown-linux-gnu-gcc"

样本Rust库

Cargo.toml内容:

[package]
name = "sample"
version = "0.1.0"
edition = "2018"

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

src/lib.rs内容:

#[no_mangle]
pub extern fn hello() {
    println!("Rust here");
}

编译并检查

编译为:

$ cargo build --release --target x86_64-unknown-linux-gnu

检查输出:

$ file target/x86_64-unknown-linux-gnu/release/libsample.so 
target/x86_64-unknown-linux-gnu/release/libsample.so: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, with debug_info, not stripped

检查库符号:

x86_64-unknown-linux-gnu-nm -D target/x86_64-unknown-linux-gnu/release/libsample.so | grep hello
0000000000003900 T hello

在Linux机器上进行测试

target/x86_64-unknown-linux-gnu/release/libsample.so复制到您的Linux机器中。

手动加载

sample.c内容:

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>

int main(int argc, char**argv) {
    void *lib;
    void (*hello)(void);
    char *error;

    lib = dlopen("./libsample.so", RTLD_NOW);
    if (!lib) {
        fprintf(stderr, "%s\n", dlerror());
        exit(-1);
    }
    dlerror();

    *(void **)(&hello) = dlsym(lib, "hello");

    if ((error = dlerror()) != NULL) {
        fprintf(stderr, "%s\n", error);
        dlclose(lib);
        exit(-1);
    }

    (*hello)();

    dlclose(lib);
    exit(0);
}

使用gcc -rdynamic -o sample sample.c -ldl进行编译并运行:

$ ./sample
Rust here

检查其是否动态链接:

$ ldd ./sample
    linux-vdso.so.1 (0x00007ffe609eb000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc7bdd69000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc7bd978000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc7be16f000)

动态链接

sample.c内容:

extern void hello(void);

int main(int argc, char **argv) {
    hello();
}

使用gcc sample.c -o sample -lsample -L.进行编译并运行:

$ LD_LIBRARY_PATH=. ./sample
Rust here

检查其是否动态链接:

$ LD_LIBRARY_PATH=. ldd ./sample
    linux-vdso.so.1 (0x00007ffc6fef6000)
    libsample.so => ./libsample.so (0x00007f8601ba3000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f86017b2000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f86015ae000)
    librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f86013a6000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8601187000)
    libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8600f6f000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f8601fd5000)

静态库

工具链

$ rustup target add x86_64-unknown-linux-musl
$ brew install filosottile/musl-cross/musl-cross

~/.cargo/config中添加以下行:

[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"

样本Rust库

Cargo.toml内容:

[package]
name = "sample"
version = "0.1.0"
edition = "2018"

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

src/lib.rs内容:

#![crate_type = "staticlib"]

#[no_mangle]
pub extern fn hello() {
    println!("Rust here");
}

编译

编译为:

$ cargo build --release --target x86_64-unknown-linux-musl

在Linux机器上进行测试

target/x86_64-unknown-linux-musl/release/libsample.a复制到您的Linux机器中。

sample.c内容:

extern void hello(void);

int main(int argc, char **argv) {
    hello();
}

使用gcc sample.c libsample.a -o sample进行编译并运行:

$ ./sample
Rust here

检查它是否静态链接:

$ ldd ./sample
    statically linked