这是一个最小化可重现示例,展示了你遇到的相同问题。我创建了一个导出简单加法函数的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
,并记住路径是相对于可执行文件而不是当前工作目录。
另请参见:
/etc/ld.so.conf
并运行ldconfig
对您不起作用吗?(如果这样做不起作用,可以尝试设置LD_LIBRARY_PATH
环境变量。) - BurntSushi5rpath
是 Rust 可以控制的(更准确地说是 Rust 指示链接器处理)。 - Shepmastercargo clean
和cargo build --verbose
命令。然后查看在链接可执行文件时是否传递了rpath
选项。 - Shepmaster