rust-lang/cc-rs

Unable to link stdc++ for example when building rust library

mjforan opened this issue · 9 comments

Problem: cc is unable to find stdc++ when building an example for a library crate.

✅ works: binary crate, call c++ from example


lib_x/
  src/
    main.rs
  examples/
    test.cpp            #include <iostream>
    lib_x_example.rs    call function in test.cpp

✅ works: library crate, call c++ from library


lib_x/
  src/
    test.cpp            #include <iostream>
    lib.rs              call function in test.cpp
  examples/
    lib_x_example.rs    call function in lib.rs

❌ does not work: library crate, call c++ from example


lib_x/
  src/
    lib.rs
  examples/
    test.cpp            #include <iostream>
    lib_x_example.rs    call function in test.cpp

build.rs:

  println!("cargo:rerun-if-changed=examples/test.cpp");
  cc::Build::new()
  .cpp(true)
  .file("examples/test.cpp")
  .cpp_link_stdlib("stdc++")
  .compile("test_cpp");

Error output:

error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/mjforan/.cargo/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/.local/bin:/home/mjforan/.local/bin:/usr/local/cuda-11.6/bin:/home/mjforan/anaconda3/bin:/home/mjforan/anaconda3/condabin:/home/mjforan/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin" VSLANG="1033" "cc" "-m64" "/tmp/rustcUr3FJl/symbols.o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976.lib_x_example.a5b620fa4eee2b6-cgu.0.rcgu.o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976.e6xnw6b1lbeu1tod6egd8g1j6.rcgu.o" "-Wl,--as-needed" "-L" "/home/mjforan/lib_x/target/release/deps" "-L" "/home/mjforan/lib_x/target/release/build/lib_x-e4aaea58acf14dea/out" "-L" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bdynamic" "-ltest_cpp" "-Wl,-Bstatic" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-1c4b19562077c20d.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-85a631ebc91746e0.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-fdace1a0b4cda412.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libmemchr-e5c28d21823e9a85.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-1e0edbcd516a8cce.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-77a1dc5e8fb357d6.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-8c9d2edb6dff139f.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-ecadd85ae8bacc0c.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-67895a0c8dd8130b.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-5b4263e767961458.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-4f03d5a171522141.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-9e4e8543de06315e.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-7fc51dfce9c057eb.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-7ec98a9b1cc1792f.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-2f9b4333f6d32438.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-b6fe0262c36c500a.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-2a862c0b1c86f483.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-500f37ee5bcf0ffe.rlib" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-06dfbf1de02fde3b.rlib" "-Wl,-Bdynamic" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/mjforan/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/mjforan/lib_x/target/release/examples/lib_x_example-06693f701c91b976" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-Wl,--strip-debug" "-nodefaultlibs"
  = note: /usr/bin/ld: /home/mjforan/lib_x/target/release/build/lib_x-e4aaea58acf14dea/out/libtest_cpp.a(b688d06bea1307b6-test.o): in function `test_cpp':
          test.cpp:(.text.test_cpp+0x19): undefined reference to `std::cout'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x21): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x47): undefined reference to `std::ostream::put(char)'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x64): undefined reference to `std::ctype<char>::_M_widen_init() const'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x89): undefined reference to `std::__throw_bad_cast()'
          /usr/bin/ld: test.cpp:(.text.test_cpp+0x55): undefined reference to `std::ostream::flush()'
          collect2: error: ld returned 1 exit status
          
  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib)

error: could not compile `lib_x` (example "lib_x_example") due to 1 previous error

Environment: Raspberry Pi OS (aarch64) and Ubuntu 24.04 (amd64) with fresh install of Rust: cargo 1.81.0, cc="1.1.18"

I have tried setting various options in build.rs such as manually specifying the path to /lib/aarch64-linux-gnu/libstdc++.so.6

Sorry I don't understand how you are calling std::cout, can you provide the code please?

Just including the header file won't work, and linking with stdc++ also won't work.

C++ ABI is different from rust with its own name demangles so that it can support function overloading.

test.cpp

#include <iostream>
extern "C" void test_cpp() {
    std::cout<<"hello"<<std::endl;
}

lib_x_example.rs

#[link(name = "test_cpp")]
extern { fn test_cpp(); }

pub fn main() {
    unsafe {test_cpp();}
}

Thanks.

And can you also show me the build.rs ?

Because you would need it to be put in the workspace, not in src or examples, but in the workspace .

Only when it's in there, it would be automatically picked up by cargo, and run by cargo to compile and link the c++ file.

Yes, build.rs is in the workspace, as well as Cargo.toml, Cargo.lock, and the target directory. I left them out of the initial issue for simplicity. The relevant section of build.rs is listed above.

Thanks.

I think you would have to define

#[link(name = "test_cpp")]
pub extern { fn test_cpp(); }

in the lib.rs, when you compile and link with an external library, you usually define the API of the external library in the library itself.

That does not work. The example still fails to compile.

Can you remove

.cpp_link_stdlib("stdc++")

I think using the default libstdc++ might work.

No, that value is correct for my platform. Removing it does not help.
https://docs.rs/cc/latest/cc/struct.Build.html#method.cpp_link_stdlib
https://docs.rs/cc/latest/cc/#c-support

Would you mind reproducing on your end? It seems you are just taking guesses here and I have already tried these things.

Another clue: when I wrap the c++ function in my lib.rs and call that method from my example, then I am also able to use the c++ function directly in my example.

Would you mind reproducing on your end? It seems you are just taking guesses here and I have already tried these things.

Sorry I don't have access to my computer for now.

Another clue: when I wrap the c++ function in my lib.rs and call that method from my example, then I am also able to use the c++ function directly in my example.

Yes, that's pretty much how most crates use cc and external crates.

Creates bindings for it in rust library crate, then call the library crate.

I think the error you experience might be due to how rust links crates.

AFAIK, cargo does a linking per crate to produce rlib, so if the external library function isn't used in the library crate, it might strip out the unused linkage to stdc++.

Since rlib isn't stablised and mostly internal to cargo, there isn't much documentation on it.

For that particular behavior, it might be faster to ask on rust's official zulip in t-cargo channel