lanctot
lanctot

Reputation: 41

Wrapping a (dynamically-linked) C library in Rust: works with rustc, not with cargo

For my project, I need to be able to run a C function that is pre-compiled into a shared library. I'm doing this on Ubuntu 20.10. I am able to successfully do this using rustc directly, but not when I used cargo.

Here is a list of my files (contents below):

Cargo.toml

[package]
name = "libtest-sys"
version = "0.1.0"
links = "test"
build = "build.rs"
edition = "2021"

[dependencies]
libc = "0.2"

[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
pkg-config = "0.3"

build.rs

fn main() {
  println!("cargo:rustc-link-search={}", "/home/lanctot/rust_test");
  println!("cargo:rustc-link-lib=dylib=test");
}

test.h

void test();

test.c

#include <stdio.h>

void test() {
  printf("This is a test!\n");
}

lib.rs

include!("./bindings.rs");

main.rs

include!("./bindings.rs");

fn main() {
  unsafe {
    test();
  }
}

Now, here's how I produce my shared library, Rust bindings, and my test program with rustc:

$ pwd 
/home/lanctot/rust_test
$ gcc -c -fpic -Iinclude src/test.c -shared -o libtest.so
$ bindgen include/test.h -o src/bindings.rs
$ cat src/bindings.rs
/* automatically generated by rust-bindgen 0.59.2 */

extern "C" {
    pub fn test();
}
$ rustc -L. -ltest src/main.rs
$ ./main
This is a test!

Sweet, it runs! But, when I try with Cargo:

$ cargo build --verbose
       Fresh pkg-config 3.24
       Fresh libc v0.2.112
       Fresh jobserver v0.1.24
       Fresh cc v1.0.72
   Compiling libtest-sys v0.1.0 (/home/lanctot/rust_test)
     Running `rustc --crate-name libtest_sys --edition=2018 src/main.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=fb122eefc998dd8f -C extra-filename=-fb122eefc998dd8f --out-dir /home/lanctot/rust_test/target/debug/deps -C incremental=/home/lanctot/rust_test/target/debug/incremental -L dependency=/home/lanctot/rust_test/target/debug/deps --extern libc=/home/lanctot/rust_test/target/debug/deps/liblibc-315c8c30d4535204.rlib --extern libtest_sys=/home/lanctot/rust_test/target/debug/deps/liblibtest_sys-13ba9453242dae24.rlib -L /home/lanctot/rust_test`
error: linking with `cc` failed: exit status: 1
  |
  = note: "cc" "-m64" "-Wl,--eh-frame-hdr" "-Wl,-znoexecstack" "-Wl,--as-needed" "-L" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.0.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.1.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.2.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.3.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.4.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.5.rcgu.o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.6.rcgu.o" "-o" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f" "/home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.4257t6mbhq03i5nc.rcgu.o" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-nodefaultlibs" "-L" "/home/lanctot/rust_test/target/debug/deps" "-L" "/home/lanctot/rust_test" "-L" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,--start-group" "-Wl,-Bstatic" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-522c175df7e65c76.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-6de0d9a99fa442f6.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-acc51f402ffb0a47.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-cbf21249c0bb64a3.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-7811673bd6c230a1.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-9d24c50c56b501a5.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-f2bf86d8579f7abb.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-a510913c7a93022a.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-113eb81ce98dbb76.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-36a8ba3e35050b5b.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-6ec306849e1e0cbe.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-cb42b322cd006390.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-598bd3c8385c4a71.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-dcf076dc617ac8b6.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-9231e3c18aac66ef.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-fb02ea9686597718.rlib" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-f203df3b7c648201.rlib" "-Wl,--end-group" "/usr/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-b0414f7c79a0b9b4.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc"
  = note: /usr/bin/ld: /home/lanctot/rust_test/target/debug/deps/libtest_sys-fb122eefc998dd8f.libtest_sys.57433ps7-cgu.0.rcgu.o: in function `libtest_sys::main':
          /home/lanctot/rust_test/src/main.rs:5: undefined reference to `test'
          collect2: error: ld returned 1 exit status
          

error: aborting due to previous error

error: could not compile `libtest-sys`

Caused by:
  process didn't exit successfully: `rustc --crate-name libtest_sys --edition=2018 src/main.rs --error-format=json --json=diagnostic-rendered-ansi --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 -C metadata=fb122eefc998dd8f -C extra-filename=-fb122eefc998dd8f --out-dir /home/lanctot/rust_test/target/debug/deps -C incremental=/home/lanctot/rust_test/target/debug/incremental -L dependency=/home/lanctot/rust_test/target/debug/deps --extern libc=/home/lanctot/rust_test/target/debug/deps/liblibc-315c8c30d4535204.rlib --extern libtest_sys=/home/lanctot/rust_test/target/debug/deps/liblibtest_sys-13ba9453242dae24.rlib -L /home/lanctot/rust_test` (exit status: 1)

I've tried many things to fix this. If I delete main.rs, it works. Also note: I do not see the required -ltest anywhere in the commands above. I've scoured many documentation pages and believe to have it all setup correctly. Since it works fine with rustc, I believe that somehow what I'm missing is how to instruct cargo to actually pass the link flag.

Upvotes: 3

Views: 3546

Answers (1)

rodrigo
rodrigo

Reputation: 98496

From the Cargo book:

The -l flag is only passed to the library target of the package, unless there is no library target, in which case it is passed to all targets. This is done because all other targets have an implicit dependency on the library target, and the given library to link should only be included once. This means that if a package has both a library and a binary target, the library has access to the symbols from the given lib, and the binary should access them through the library target's public API.

And indeed your package has both a lib and a bin: src/lib.rs, src/main.rs.

The solution is, as the book says, not to use the C symbols in the binary but only in the library. The rationale is that when a crate has both a lib and a bin, the lib is considered the main part (only one, that is used when used as dependency), and the bin is a helper.

Upvotes: 1

Related Questions