I have a shared library that I'd like to dynamically link into several separate binary Cargo applications. I include its location in the linker using the -- -L /path/to/dir
format and the application compiles correctly with the significant decrease in binary size I expect. However, when checking the generated binary using ldd
, I get a message saying that the library couldn't be found:
casey@Gilthar-II:~/bot4/backtester/target/release$ ldd backtester
linux-vdso.so.1 => (0x00007ffc642f7000)
libalgobot_util.so => not found
If I add the library to the /lib/x86_64-linux-gnu
directory, the application runs without issue.
Is there a way to get Rust to look for .so files in the same directory as the binary or in a directory like lib in the binary's directory to be loaded at runtime? If that's not possible, is there a way to at least get Rust to insert the absolute path of the library it was linked with?
I've tried setting rpath = true
with no effect.
Note that Rust is, in a very technical and useless sense, dynamically linked on Linux; while all the Rust code forms a single object, that object is dynamically linked with the C code it depends on at runtime, including the VDSO and libc.
Dynamic libraries are linked during the execution of the final executable. Only the name of the dynamic library is placed in the final executable. The actual linking happens during runtime, when both executable and library are placed in the main memory.
While rust provides linking against shared libraries, it does not provide the ability to load them at runtime. If you only want to use shared libraries that you know about before runtime, you may find not find this crate very useful.
By using dynamic linking, you can upgrade the routines in the shared libraries without relinking. This form of linking is the default and no additional options are needed. Static linking means that the code for all routines called by your program becomes part of the executable file.
Here's a Minimal, Reproducible Example that exhibits the same problem that you experienced. I created a C library exporting a simple addition function. I also created a Cargo project to use this function.
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;
}
The library was compiled as 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
Investigating further, I asked the Rust compiler to print out the linker args it was going to use:
cargo rustc -- --print link-args
This printed out a bunch of stuff, but an important line was:
"-Wl,-rpath,$ORIGIN/../../../../../../.rustup/toolchains/nightly-aarch64-unknown-linux-gnu/lib/rustlib/aarch64-unknown-linux-gnu/lib"
This is a directive to the linker to add specific values to the rpath of the finished binary. Missing is any reference to the dynamic library that we are linking to. In retrospect, this probably makes sense, as how would the compiler know that we want to include it in the rpath?
A workaround is to add another directive to the linker. There are interesting options (like $ORIGIN
), but for simplicity, we will just use an absolute path:
cargo rustc -- -C link-args="-Wl,-rpath,/home/shep/rust/dynlink/library/"
And the resulting binary prints the right thing for ldd
and runs without setting 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
Turning to making it relative, we can use $ORIGIN
:
cargo rustc -- -C link-args='-Wl,-rpath,$ORIGIN/../../../library/'
Be careful to escape $ORIGIN
properly for your shell, and remember that the path is relative to the executable, not the current working directory.
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With