将 Rust 应用程序与不在运行时链接器搜索路径中的动态库链接
Linking Rust application with a dynamic library not in the runtime linker search path
我有一个共享库,我想将其动态 link 到几个单独的二进制 Cargo 应用程序中。我使用 -- -L /path/to/dir
格式将它的位置包含在 linker 中,应用程序编译正确,二进制大小显着减少,我预计。但是,当使用 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 在与二进制文件相同的目录中或二进制文件目录中的 lib 目录中查找 .so 文件以在运行时加载?如果那不可能,有没有办法至少让 Rust 插入库的绝对路径link?
我试过设置 rpath = true
但没有效果。
这里有一个 Minimal, Reproducible Example 与您遇到的问题相同。我创建了一个导出简单加法函数的 C 库。我还创建了一个 Cargo 项目来使用这个功能。
dynlink/
├── executable
│ ├── build.rs
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── 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/main.rs
extern {
fn from_the_library(a: u8, b: u8) -> u8;
}
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=/home/shep/rust/dynlink/library");
}
Cargo.toml
[package]
name = "executable"
version = "0.1.0"
edition = "2021"
[profile.dev]
rpath = true
进一步调查,我要求 Rust 编译器打印出它要使用的链接器参数:
cargo rustc -- --print link-args
这打印了一堆东西,但重要的一行是:
"-Wl,-rpath,$ORIGIN/../../../../../../.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib"
这是链接器的指令,用于将特定值添加到已完成二进制文件的 rpath。缺少对我们链接到的动态库的任何引用。回想起来,这可能是有道理的,因为编译器如何知道我们要将它包含在 rpath 中?
解决方法是向链接器添加另一个指令。有一些有趣的选项(如 $ORIGIN
),但为了简单起见,我们将只使用绝对路径:
cargo rustc -- -C link-args="-Wl,-rpath,/home/shep/rust/dynlink/library/"
并且生成的二进制文件为 ldd
打印正确的内容并且在不设置 LD_LIBRARY_PATH
:
的情况下运行
$ ldd ./target/debug/executable | grep awesome
libawesome_math.so => /home/shep/rust/dynlink/library/libawesome_math.so (0x0000ffffb1e56000)
$ ./target/debug/executable
Adding: 3
转向相对,我们可以使用$ORIGIN
:
cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../library/'
小心为您的 shell 正确转义 $ORIGIN
,并记住路径是相对于 可执行文件 ,而不是当前工作目录。
另请参阅:
添加到 (显然我没有足够的声誉来评论):我不确定这个功能是什么时候添加的,但至少从 开始 Rust 1.20,可以通过设置环境变量RUSTFLAGS
:
达到同样的效果
$ RUSTFLAGS="-C link-args=-Wl,-rpath,/the/lib/path" cargo build
这可能比 cargo rustc
选项更方便,例如,如果您正在使用仅调用 cargo build
.
的构建脚本
我有一个共享库,我想将其动态 link 到几个单独的二进制 Cargo 应用程序中。我使用 -- -L /path/to/dir
格式将它的位置包含在 linker 中,应用程序编译正确,二进制大小显着减少,我预计。但是,当使用 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 在与二进制文件相同的目录中或二进制文件目录中的 lib 目录中查找 .so 文件以在运行时加载?如果那不可能,有没有办法至少让 Rust 插入库的绝对路径link?
我试过设置 rpath = true
但没有效果。
这里有一个 Minimal, Reproducible Example 与您遇到的问题相同。我创建了一个导出简单加法函数的 C 库。我还创建了一个 Cargo 项目来使用这个功能。
dynlink/
├── executable
│ ├── build.rs
│ ├── Cargo.lock
│ ├── Cargo.toml
│ └── 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/main.rs
extern {
fn from_the_library(a: u8, b: u8) -> u8;
}
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=/home/shep/rust/dynlink/library");
}
Cargo.toml
[package]
name = "executable"
version = "0.1.0"
edition = "2021"
[profile.dev]
rpath = true
进一步调查,我要求 Rust 编译器打印出它要使用的链接器参数:
cargo rustc -- --print link-args
这打印了一堆东西,但重要的一行是:
"-Wl,-rpath,$ORIGIN/../../../../../../.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib"
这是链接器的指令,用于将特定值添加到已完成二进制文件的 rpath。缺少对我们链接到的动态库的任何引用。回想起来,这可能是有道理的,因为编译器如何知道我们要将它包含在 rpath 中?
解决方法是向链接器添加另一个指令。有一些有趣的选项(如 $ORIGIN
),但为了简单起见,我们将只使用绝对路径:
cargo rustc -- -C link-args="-Wl,-rpath,/home/shep/rust/dynlink/library/"
并且生成的二进制文件为 ldd
打印正确的内容并且在不设置 LD_LIBRARY_PATH
:
$ ldd ./target/debug/executable | grep awesome
libawesome_math.so => /home/shep/rust/dynlink/library/libawesome_math.so (0x0000ffffb1e56000)
$ ./target/debug/executable
Adding: 3
转向相对,我们可以使用$ORIGIN
:
cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../library/'
小心为您的 shell 正确转义 $ORIGIN
,并记住路径是相对于 可执行文件 ,而不是当前工作目录。
另请参阅:
添加到 RUSTFLAGS
:
$ RUSTFLAGS="-C link-args=-Wl,-rpath,/the/lib/path" cargo build
这可能比 cargo rustc
选项更方便,例如,如果您正在使用仅调用 cargo build
.