Linking problems in Ruby gem using a Rust staticlib


#1

Hi there!

I’m trying to compile a Rust staticlib with musl, but when I remove #![no_std] things start to go wrong.

I created an automated example project (based on steveklabnik/rust_example) to illustrate my problem.

I want to use the Rust staticlib in a Ruby gem, but when the staticlib is being linked by Ruby it throws the following error:

$ ruby extconf.rb
checking for rust_example_init() in -lrust_example... yes
creating Makefile
$ make clean
$ make
compiling rust_example.c
linking shared-object rust_example/rust_example.so
/usr/lib/gcc/x86_64-alpine-linux-musl/5.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: ./librust_example.a(__stdout_write.lo): relocation R_X86_64_PC32 against protected symbol `__stdio_write' can not be used when making a shared object
/usr/lib/gcc/x86_64-alpine-linux-musl/5.3.0/../../../../x86_64-alpine-linux-musl/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
Makefile:254: recipe for target 'rust_example.so' failed
make: *** [rust_example.so] Error 1

Is it possible to create a staticlib with musl like this and use it in a Ruby project?
Or can I only use it without the standard library, #![no_std]?

Help is very much appreciated :slight_smile:


#2

So great news, I just did this for the Skylight gem! So, it is possible.

The build process of your example looks a bit different than ours. What we’ve done is build a .so file with roughly the following steps (no guarantees this actual code would run verbatim):

# Build libskylight.a
cargo build --release --target x86_64-unknown-linux-musl
# Extract .o files from relevant static libraries
echo "libskylight.a libcrypto.a libcurl.a libssl.a" | xargs -n 1 ar x
# Combine all .o files into a .so
musl-gcc --shared -Wall -o libskylight.so *.o -lrt -ldl -lpthread -lgcc_s -lc -lm

Then in some C code for our Ruby gem, we actually dlopen and dlsym to link up the shared library.

This admittedly is a bit complex and to be honest, I don’t know all the rationale for why we went the .so route as those decisions were made prior to my involvement in the code. I’d love to explore a bit more and try to figure out the costs and benefits, but for now I’ve decided to stick with what works.

At this point we have a different .so for musl than we do for glibc-based Linux. As part of the gem install process we download the appropriate .so for the environment. If you’re curious, our Ruby code is on Github. The Rust code isn’t public, but I’ve put the generated dynamic loading code in a gist.

Again, I’m not sure this is the best way to do things, but it does work!


#3

Great to hear it works for Skylight!

I’m still trying to figure out what exactly your approach does (it’s Friday afternoon here now :wink: )
but from what I can tell is that it’s eventually creating a dynamic lib instead of a static lib?


#4

That’s correct. We’re loading the Rust section as a dynamic lib.