Linking against C++ dependencies

#1

I’m the author of the (spatialite-sys)[https://gitlab.com/ndarilek/spatialite-sys] crate. As far as I know, I’m its only consumer, and until recently I’ve been building all necessary dependencies with a build.sh that dumps everything in a shared prefix, then makes that prefix available to my dependent program’s build phase. Yesterday I decided to vendor a whole bunch of dependencies and invoke autotools from the build.rs, thus moving the build process into something managed by Cargo.

If I check out the crate and run cargo build, everything works. If I depend on the crate externally, however, I get:

  = note: /home/nolan/Projects/Waynav/Engine/target/debug/deps/libspatialite_sys-7559e698af999ec9.rlib(libgeos_c_la-geos_ts_c.o): In function `void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag) [clone .isra.99]':
          /usr/include/c++/7/bits/basic_string.tcc:219: undefined reference to `std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)'
          /usr/include/c++/7/bits/basic_string.tcc:212: undefined reference to `std::__throw_logic_error(char const*)'
          ... (Lots of similar errors omitted)

One of my dependencies is geos, which is a C++ library. I’m guessing based on these errors that I have to link against C++'s STL somehow? If so, any pointers on how to do that? Guessing I need to add some incantation to build.rs, but I’m not sure what that’d be in the case of statically linking in the C++ STL.

I did a bit of googling on the subject, and read some things about setting my crate type. Do I need to do that in this case? I’m not building an external library to be consumed from other C/C++ code, just a convenience crate for making SpatiaLite initializers and functions available from Rust-hosted SQLite databases.

Thanks for any help.

#2

Hey folks, wanted to bump this post, update where I am with this, and see if I could get any help.

I found this post which pointed me to how to dynamically link in the C++ STL. This worked, in that my binary and shared libraries all dynamically linked against stdc++. However, I’m now trying to cross-compile on Android, and Android doesn’t include a standard C++ runtime. As such, I’d like to switch to statically linking. The alternative is to ship a shared C++ library alongside the one I build, and I’m not sure how well that’d work with JNI.

First I tried:

println!("cargo:rustc-link-lib=static=stdc++");

This gives:

error: could not find native static librarystdc++, perhaps an -L flag is missing?

I do have a libstdc++.a file in the same directory as my libstdc++.so, /usr/lib/gcc/x86_64-linux-gnu/7/libstdc++.a, so I’m not clear what’s going on here. Presumably if dylib found stdc++ then static should find it too.

I’m using -static-libstdc++ in various instances of my LDFLAGS in the build script. I’m not clear what this does above and beyond statically linking to libstdc++.a, but it’s made lots of STL link errors in my build dependencies go away, so I need to find an equivalent for Cargo.

Any tips on what to do here would be greatly appreciated. I’ve been slugging my way through issue after issue trying to wrap this autoconf build process into build.rs, and don’t have much hair left to pull out. :slight_smile: Thanks.

#3

If you’re cross-compiling to Android, you’ll need to link to a stdc++ cross-compilied for Android as well. This will not be in your regular system lib directory (/usr/lib/gcc/x86_64-linux-gnu). I imagine this might be shipped with the Android NDK.

#4

Yeah, in this instance I’m not cross-compiling though. I’m trying to
compile things on the host first. I have a binary in the crate that
fails to build because of STL link issues, and I’m working on getting
that fixed before moving on to cross-compilation. I just mentioned
cross-compilation since, without that need, I’d just dynamically link to
stdc++ and be off to the races.

#5

When your build script outputs cargo:rustc-link-lib=static=... while building a library crate, this prompts rustc to find the archive then and there and include the entire static library in the crate. I think what you want is that the library is statically linked in the final build product. I think there’s a way to do this, but I can’t quite remember it now.

#6

Yup, there’s a nightly feature that lets you specify exactly this: static_nobundle.

Put this in your build script:

println!("cargo:rustc-link-lib=static-nobundle=stdc++");

And this in your lib.rs:

#![feature(static_nobundle)]
#7

As far as I’m aware, you need a .a file to statically link. The .so file is only for dynamic linking.

#8

Hmm, are you saying that I add this to my library, and that static linking will be done when the crate is used, not when it is built? I’m still a bit confused about how to statically link in c++/stdc++. Right now I’m building the crate directly to test things out, and even there I can’t figure out how to statically pull in a C++ runtime.

Also, do I need to be looking at Clang/LLVM’s C++ runtime rather than GNU’s? Just discovered that I didn’t have libc+±dev installed, which gives me several instances of libc++.a. Is there something I need to do to tell a locally installed rustc (I.e. in my home directory via rustup) where to find these system-wide libraries, or should it be piggy-backing on my system-wide compiler installations?

FWIW, clang++ -lc++ -static produces:

/usr/bin/../lib/gcc/x86_64-linux-gnu/7.3.0/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'

So my system-wide compiler seems to work fine with the defaults. This makes me wonder if rustc bundles its own LLVM or something else.

Thanks for all the help so far.

#9

Right, but the C++ runtime needs to be statically linked into my dynamic library alongside all of its additional dependencies for easier cross-platform use.

1 Like
#10

OK, looks like progress! I’d held off attempting to integrate static_nobundle because I thought that’d just leave me linking against a static library rustc couldn’t find, but it’s now finding the library fine, so that may be my fix.

Now I’m running into other issues related to pthread so I can’t confirm that things definitely work, but I’ll check back in once those are resolved.

Thanks, I’ve been scouring the web for a fix to this. :slight_smile:

#11

Yup, can confirm that this worked. I also rage-quit GCC, which fixed a few other issues. I’m now getting binaries that run and appear to have C++ statically linked in.

Thanks for pointing me to this feature!