Rust is an amazing language, with amazing community and tooling.
However, sometimes, you can end up in complex situation where the standard tools are not sufficient anymore, in those case is necessary to dive deeply into compilers, a topic that can be quite difficult for some developers, especially if they come from a world where compilers are not so common, like web programming.
The rust compiler dynamically link the executable against the glibc in the system.
Hence if you compile your software against a newer version of glibc (say 2.19) that the one available where you run the executable (say in the host is available 2.14) it may not work.
The possible solutions
There are several solution to this problem.
The cleanest one is to don’t dynamically link against glib, indeed is possible to compile a rust binary statically linking musl, to do so is sufficient to compile against the correct target, usually using
cargo build --target x86_64-unknown-linux-musl
Another possibility is to compile in an environment with an “old-enough” version of glibc, this is usually done using docker and indeed there is a whole project that aim to create a set “zero setup” docker images.
It is more complex than this
Unfortunately my requirements for RediSQL ( fastest, simplest, in-memory SQL) are a little more complex.
In first place it is impossible to compile against musl, indeed I am building a
cdylib (system dynamic library) which is not supported by the musl targets.
The option of using a docker container is appealing, unfortunately I need
bindgen in order to compile RediSQL that requires a modern version of LLVM and I wasn’t able to find or quickly generate an image with the correct combination of modern LLVM and very old glibc.
The last option I got was to create myself a custom toolchain.
A toolchain is the set of tools, libraries, etc, necessary to compile stuff.
Building a toolchain from scratch is somehow resource heavy, indeed is necessary to compile, several times, a compiler; moreover is a quite complex task and is necessary to be very careful with paths, names and environment variables.
Fortunately crosstool-ng helps enormously. It automatize all the step necessary to build a functionally toolchain leaving to us the only chore of selecting, among the other options, what architecture, operative system, compiler version, and crucially glibc version, we want in our custom toolchain.
The downside of crosstool-ng is that if something fails is complex to understand how to fix it.
The result of crosstool-ng is a directory that contains our toolchain with all the necessary software, libraries, etc…
Using the custom toolchain
Ok, now we got our “C” toolchain, but how does it help in a rust project?
The crucial step is to tel
cargo to link the dynamic library with the compiler we have just generated that use the glibc version we selected with crosstool-ng.
Fortunately we can set
rustc and so
cargo to use whatever linker we want, by default it use whatever is set in your system
Setting the linker can be done using environment variables:
RUSTFLAGS="-C linker=~/x-tools/x86_64-unknown-linux-gnu/bin/x86_64-unknown-linux-gnu-gcc" cargo build --release
Putting all together
The last step is to make this whole process simple to reproduce, and at this point we can rely on docker containers.
We create a container with the toolchain inside, install all the necessary rust software in it, and use it as base to compile RediSQL.
Inside the toolchain the executable
x86_64-unknown-linux-gnu-ct-ng.config will allow to get the configuration used by crosstool-ng to generate the toolchain itself, so that can be adapted to your use case.
Finally the docker image is available here: redbeardlab/redisq-builder.