【问题标题】:Cross-compile and link a dynamic library (cdylib) on macOS for Linux with cargo and rust在适用于 Linux 的 macOS 上使用 cargo 和 rust 交叉编译和链接动态库 (cdylib)
【发布时间】:2019-12-30 12:21:03
【问题描述】:

我有一个 Rust 库,它为 FFI 公开了几个函数。我假设我必须将 crate-type 设置为 cdylib - 因为我想从 Ruby 和 PHP 调用这些函数(通过ffi ruby​​ gem)。 但我无法将它从 OSX 交叉编译到 Linux。我试图关注一些使用musl libctutorials - 这是针对静态库的,但我没有找到其他任何东西。

所以链接器是这样定义的:

# .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 不支持这些板条箱类型?有可能吗?

【问题讨论】:

    标签: rust cross-compiling ffi musl


    【解决方案1】:

    问题

    我有一个 Rust 库,它为 FFI 公开了几个函数。因此我必须将 crate-type 设置为 cdylib

    您从哪里获得这些信息?您可以创建动态库 (.so) 或静态库 (.a)。 Alex 有一个包含大量示例的存储库:rust-ffi-examples

    musl 用于您确实想要创建二进制文件的情况,该二进制文件是静态链接的并且完全没有依赖关系 (staticlib)。一切都在二进制文件中。你把它扔到 Linux 盒子上,它就会起作用。

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

    交叉编译

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

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

    有很多方法可以实现您想要的。请参阅@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
    

    【讨论】:

    • 我通常不会为交叉编译而烦恼——真正的答案。更简单的是,只需使用支持 Windows / Linux / macOS 的 CI 提供程序并在那里构建,将二进制文件上传到某处。
    • 这非常非常有帮助,即使最后(也许)我不会按照你的建议使用交叉编译,你的例子帮助我学到了很多东西。
    • 有一个错字,请检查最后的编辑(.so -> .a 如果是静态的)。另请查看repo,Rust 和 FFI 的示例。
    • @zrzka 我能够成功编译cdylib 库并将其与来自Ruby 和PHP 的动态链接一起使用——这是我的实际目标。至于 crate 类型,我认为这是正确的选择,基于:doc.rust-lang.org/reference/linkage.html#linkage
    猜你喜欢
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    • 2020-12-23
    • 2016-12-08
    • 1970-01-01
    相关资源
    最近更新 更多