将Rust应用程序与不在运行时链接程序搜索路径中的动态库链接

时间:2016-11-15 05:33:09

标签: rust dynamic-linking dynamic-library rust-cargo ldd

我有一个共享库,我想动态链接到几个单独的二进制货物应用程序。我使用-- -L /path/to/dir格式在链接器中包含它的位置,并且应用程序正确编译,我期望的二进制大小显着减少。但是,在使用ldd检查生成的二进制文件时,我收到一条消息,指出无法找到该库:

casey@Gilthar-II:~/bot4/backtester/target/release$ ldd backtester 
    linux-vdso.so.1 =>  (0x00007ffc642f7000)
    libalgobot_util.so => not found

如果我将库添加到/lib/x86_64-linux-gnu目录,则应用程序运行时没有问题。

有没有办法让Rust在与二进制文件相同的目录中查找.so文件,或者在二进制文件目录中的lib目录中查找要在运行时加载的文件?如果那是不可能的,有没有办法至少让Rust插入它所链接的库的绝对路径?

我尝试设置rpath = true没有效果。

2 个答案:

答案 0 :(得分:14)

这是一个完整的解决方案......

我创建了一个导出简单附加功能的C库。我还创建了一个Cargo项目来使用这个功能。

/scratch
├── executable
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── build.rs
│   ├── src
│   │   ├── main.rs
└── library
    ├── awesome_math.c
    └── libawesome_math.so

<强> awesome_math.c

#include <stdint.h>

uint8_t from_the_library(uint8_t a, uint8_t b) {
  return a + b;
}

该库编译为gcc -g -shared awesome_math.c -o libawesome_math.so

<强> src.rs

extern crate libc;

extern {
    fn from_the_library(a: libc::uint8_t, b: libc::uint8_t) -> libc::uint8_t;
}

fn main() {
    unsafe {
        println!("Adding: {}", from_the_library(1, 2));
    }
}

<强> build.rs

fn main() {
    println!("cargo:rustc-link-lib=dylib=awesome_math");
    println!("cargo:rustc-link-search=native=/scratch/library");
}

<强> Cargo.toml

[package]
name = "executable"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]
build = "build.rs"

[dependencies]
libc = "*"

[profile.dev]
rpath = true

所有这些都表现出您遇到的相同问题。 这称为Minimal, Complete, and Verifiable Example,您在提问时应提供一个。如果提供了这个,那么这个答案可能是在12小时前创建的。

进一步调查,我要求Rust编译器打印出它将要使用的链接器args:

cargo rustc -- -Z print-link-args

这打印出了一堆东西,但两条重要的行是

"-Wl,-rpath,$ORIGIN/../../../../root/.multirust/toolchains/stable-2016-11-08-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib"
"-Wl,-rpath,/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib"

这些是链接器的指令,用于将特定值添加到已完成二进制文件的rpath中。缺失是对我们链接到的动态库的任何引用。回想起来,这可能是有意义的,因为编译器如何知道我们想要将它包含在rpath中?

解决方法是向链接器添加另一个指令。有一些有趣的选项(如$ORIGIN),但为简单起见,我们将使用绝对路径:

cargo rustc -- -C link-args="-Wl,-rpath,/scratch/library/"

生成的二进制文件为ldd打印正确的内容并在不设置LD_LIBRARY_PATH的情况下运行:

$ ldd target/debug/executable | grep awesome
    libawesome_math.so => /scratch/library/libawesome_math.so (0x00007fe859085000)
$ ./target/debug/executable
Adding: 3

转向使其相对,我们可以使用$ORIGIN

cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../library/'

小心地为shell正确转义$ORIGIN,并记住路径是相对于可执行文件而不是当前工作目录。

答案 1 :(得分:4)

添加到what Shepmaster said(显然我没有足够的声誉来评论):我不确定何时添加了此功能,但至少至少 Rust 1.20,你通过设置环境变量RUSTFLAGS

可以达到同样的效果
$ RUSTFLAGS="-C link-args=-Wl,-rpath,/the/lib/path" cargo build

这比cargo rustc选项更方便,例如,如果你使用的是只调用cargo build的构建脚本。