Linking Rust application with a dynamic library not in the runtime linker search path

大憨熊 提交于 2019-11-27 20:54:58
Shepmaster

Here's a complete solution...

I created a C library exporting a simple addition function. I also created a Cargo project to use this function.

/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;
}

The library was compiled as 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

Doing all of this exhibits the same problem that you experienced. This is called a Minimal, Complete, and Verifiable Example and you should provide one when asking a question. If this were provided, this answer might have been created 12 hours earlier.

Investigating further, I asked the Rust compiler to print out the linker args it was going to use:

cargo rustc -- -Z print-link-args

This printed out a bunch of stuff, but the two important lines were

"-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"

These are directives 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,/scratch/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 => /scratch/library/libawesome_math.so (0x00007fe859085000)
$ ./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.

Adding to what Shepmaster said (apparently I don't have enough reputation to comment): I'm not sure when this feature was added, but as of at least Rust 1.20, you can achieve the same effect by setting the environment variable RUSTFLAGS:

$ RUSTFLAGS="-C link-args=-Wl,-rpath,/the/lib/path" cargo build

This can be more convenient than the cargo rustc option if, for instance, you're using build scripts that just invoke cargo build.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!