Introduction
I was required to produce a x86-64 statically linked Rust binary for a containerized workload at work. This guide briefly outlines the process of doing so using musl, a lightweight C library. Unlike traditional compilation with glibc, musl eliminates dynamic dependencies, resulting in a single, external dependency-free executable.
A quick word on why musl is preferred over glibc
Even if a program is statically linked with glibc, it still requires to dynamically load the c library in order to satisfy loading of some NSS and iconv modules. ref
Steps
- Create a new cargo project
$ cargo new --bin static-rust-binary
- Build the default binary
$ cd static-rust-binary
$ cargo build
- Check it is dynamically linked
$ file ./target/debug/static-rust-binary
./target/debug/static-rust-binary: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=774a033de72094a3ea8a7ac99261b57a86603173, for GNU/Linux 3.2.0, with debug_info, not stripped
- Change build target to musl and re-compile
$ cargo build --release --target=x86_64-unknown-linux-musl
- Check if the binary is now statically linked
$ file ./target/x86_64-unknown-linux-musl/release/static-rust-binary
./target/x86_64-unknown-linux-musl/release/static-rust-binary: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, BuildID[sha1]=e3880597fbf7adcb623d246b69c52d9aee6fe867, with debug_info, not stripped
🎩✨ Voilà! ✨🎩
The statically linked rust binary is ready which can now be put in a scratch container to be run on #docker #kubernetes etc.
‼️ Attention! ‼️
In my experience I've run into trouble generating a statically linked binary if the project dependencies have implicitly or explicitly included openssl-sys
crate. For example -
$ cargo build --release --target=x86_64-unknown-linux-musl
...
Compiling openssl v0.10.63
...
Compiling openssl-sys v0.9.99
error: failed to run custom build command for `openssl-sys v0.9.99`
In this case the simplest way I've been able to fix the problem is to explicitly add the openssl crate (latest version) with the features = vendored
conditional dependency to the Cargo.toml
(Note: as of today "v0.10.63" is the latest version of openssl crate).
[dependencies]
...
openssl = { version = "0.10.63", features = ["vendored"] }
Re-running the build compiles successfully.
$ cargo build --release --target=x86_64-unknown-linux-musl
Compiling openssl v0.10.63
...
Compiling openssl-sys v0.9.99
...
Compiling static-rust-binary v0.1.0 (/home/abhishek.pareek/src/rust/simple-static-rust-binary)
🤞 Hopeful this will help someone get unstuck in the future! 🌟 And don't hesitate to comment if this approach worked or not! 🚀
Top comments (0)