Shared library info from build.rs not forwarded to executable

In a crate I made, the build.rs tells the compiler to add the path and name of the necessary shared library.

When I build, a file target/debug/build/vzense-rust-.../ouput is created and contains exactly the information build.rs generated with the correct paths

cargo:rustc-link-search=/home/hannes/.cargo/registry/src/index.crates.io-6f17d22bba15001f/vzense-rust-0.1.3/vzense-lib/
cargo:rustc-link-arg=-Wl,-rpath,/home/hannes/.cargo/registry/src/index.crates.io-6f17d22bba15001f/vzense-rust-0.1.3/vzense-lib/
cargo:rustc-link-lib=vzense_api

But the executable doesn't seem to have that information, cuz when I run it I get

error while loading shared libraries: libvzense_api.so: cannot open shared object file: No such file or directory

How can I get that lib info into the executable?

When I build the crate locally, there is no problem, only when I include it from crates.io registry.

Ok, one step forward.
According to the Cargo reference regarding dynamic library paths

Paths outside of the target directory are removed.

So that's why the paths were not accepted. So I followed this hack from another crate and copy the shared libraries to target/<buildType>/deps/ in build.rs.

If there is a better way to do this, that would be great though.

At least now when I use the crate in another project and do cargo run, it finds the libraries. Yay!

But... the executable is not self contained. It only works if Cargo sets the LD_LIBRARY_PATH. I do all the rustc-link-arg and -rpath stuff in build.rs, and if I build the example included in the crate itself, the executable is self-contained. What's the difference?

Then again, I ask myself why I really have to do all of this copying to deps?

Should the -rpath not work for ANY path? It should inject the path info into the executable and has nothing to do with Cargo, right? Could somebody please confirm this?

Then the actual question is, if I set an -rpath in the build.rs of a crate, should this info not be forwarded to the executable build in a project which uses the crate?

Reading this discussion about rpath, I get the impression that there is no proper way to do this in Rust yet?

Dusting off my somewhat aged knowledge about linking here. You create a dynamically linked binary, linking against the .so version of the library. That's the traditional way of linking on Unix, as it allows using one library for many binaries, keeping binary sizes small. One can even update the library without recompiling the binary.

Now times have changed, people no longer care about binary sizes and update-ability, they want self-contained binaries. One achieves this by static linking. This is done by adding the .a version of the library like any other compiled object file to the linking process. My best guess is something like

cargo:rustc-link-search=[...]/vzense-rust-0.1.3/vzense-lib/
cargo:rustc-link-arg=-Wl,-static,[...]/vzense-rust-0.1.3/vzense-lib/
cargo:rustc-link-lib=vzense_api

This -static is the key distinction here.

Hope that helps, even if it probably requires additional research.

1 Like

Thanks @Traumflug. Yes, this makes all good sense.

Unfortunately, the dynamic library (.so) is all I have. The sdk of the camera vendor provides only the dynamic library and there is no way of creating a static one from a dynamic one.

I'm not sure about this, but I think static libraries can somehow be reverse engineered to read the source, so many companies would not share those for intellectual property concerns.

IIRC, dynamic libraries contain the same as static libraries, plus some info to allow this linking at runtime. Maybe that vendor isn't up to the latest fashion, yet (this new fashion makes no sense to me either, still it exists).

Converting a dynamic to a static one should be possible, it's just rarely needed. A quick googling brings up ELF Statifier.

That said, having only a dynamic library your other options are to a) bundle your app somehow and b) depend on that dynamic library. It should be possible to link against the version copied into the Rust project and later use the system-wide version, with an appropriate LD_LIBRARY_PATH each time. The dynamic linker has a number of built in paths, on a plain Debian installation LD_LIBRARY_PATH isn't set at all. See ldconfig(8).

The worrying thing is lack of licence on the library. (Should not be putting it on crates.io without one.)

No. You have to set it up manually per executable.
There is Build Scripts - The Cargo Book that might help transferring some config.

If using rpath, you should avoid absolute paths so stick to starting at "." and "$ORIGIN"

1 Like

The worrying thing is lack of licence on the library.

That's right, thanks for pointing that out @jonh! I totally missed that.

I asked the ppl at the Vzense SDK repo if it would be possible to add a license. Other recent Vzense repos have a BSD 3-Clause license, so I hope it is possible.
I also yanked the crates and wrote to crates.io.