cross compiling and statically linking against Rust libraries
At CSIS we have traditionally written our back-end in Python, together with some C/C++ code for our Incident Response Toolkit.
A few years ago, mainly due to performance reasons, we started rewriting specific back-end services from Python to Rust, with great success. Now, for the sake of ease of development and testing, we are exploring the idea of moving parts of our C/C++ code base to Rust, too.
In order to do so, instead of re-writing everything in one swoop, we decided to try integrating Rust into our existing code base.
Following is a summary of our experiments, and a skeleton for writing a Rust library and calling it from a C/C++ application.
Targets
In order to be able to cross compile code, we need to first make sure that we have the desired targets installed. A list of available targets can be obtained via rustc --print target-list
and new ones can be installed via rustup target add <target>
.
Installed targets can be shown with rustup show
:
$ rustup show
Default host: x86_64-unknown-linux-gnu
installed toolchains
--------------------
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu
installed targets for active toolchain
--------------------------------------
i686-pc-windows-gnu
i686-pc-windows-msvc
i686-unknown-linux-gnu
i686-unknown-linux-musl
x86_64-pc-windows-gnu
x86_64-pc-windows-msvc
x86_64-unknown-linux-gnu
x86_64-unknown-linux-musl
active toolchain
----------------
stable-x86_64-unknown-linux-gnu (default)
rustc 1.31.0 (abe02cefd 2018-12-04)
Project Setup
Once the targets have been properly setup we need to setup our project. Starting with Cargo.toml
:
[package]
name = "mylib"
version = "0.1.0"
authors = ["john doe<jdo@csis.dk>"]
[dependencies]
libc = "*"
[lib]
crate-type = ["staticlib"]
Now the source code for our library src/lib.rs
:
extern crate libc;
use libc::uint32_t;
#[no_mangle]
pub extern "C" fn addition(a: uint32_t, b: uint32_t) -> uint32_t {
a + b
}
And the code for our application caller.cpp
:
#include <stdio.h>
#include <stdint.h>
extern "C" {
uint32_t
addition(uint32_t, uint32_t);
}
int main(void) {
uint32_t sum = addition(1, 2);
printf("%u\n", sum);
}
If this were a C application, only the extern "C"
needed to be removed.
Next we need to indicate our desired target, which is done in .cargo/config
, for example:
[build]
target="i686-pc-windows-gnu"
Linux binaries
In order to statically link Linux binaries the target needs to be set to -linux-musl
.
.cargo/config
[build]
target="i686-unknown-linux-musl"
Alternatively we could choose x86_64-unknown-linux-musl
for 64 bit binaries.
- build
$ g++ -m32 -c -o linux_cpp_caller.o caller.cpp
$ g++ -static -m32 linux_cpp_caller.o -o linux_cpp_caller \
-L./target/i686-unknown-linux-musl/debug/ -lmylib
If we were targeting 64 bit, the -m32
option needed to be removed and the -L
flag adjusted accordingly.
Windows Binaries
In order to statically link Windows binaries the target needs to be set to -pc-windows-gnu
. In the case of 32 bit binaries special care is needed with regards to the exception handling model. A good summary can be found on the “rust-lang” Github repository and on stack overflow.
.cargo/config
[build]
target="i686-pc-windows-gnu"
rustflags = "-C panic=abort"
Alternatively we could choose x86_64-pc-windows-gnu
for 64 bit binaries. If this were the case, the rustflags
option could be removed.
- build
$ i686-w64-mingw32-g++ -c -o win_cpp_caller.o caller.cpp
$ i686-w64-mingw32-g++ -static win_cpp_caller.o -o win_cpp_caller \
-L./target/i686-pc-windows-gnu/debug/ \
-lmylib \
-ladvapi32 \
-lws2_32 \
-luserenv
If the target was a 64bit environment, x86_64-w64-mingw32-g++
should be used instead and the -L
flag adjusted accordingly.
Conclusion
With this post we hope to have provided a simple, working example of how Rust and C/C++ can inter-operate, which can be used as a starting point.
We would like to thank the contributors of the resources linked below.
Resources
This post was a summary of the information found in the following links
- https://doc.rust-lang.org/cargo/reference/manifest.html#building-dynamic-or-static-libraries
- https://www.reddit.com/r/rust/comments/8vnf2h/statically_linking_a_rust_program_for_c/
- https://stackoverflow.com/a/15685229
- https://github.com/rust-lang/rust/issues/32859
- https://github.com/rust-lang/rust/pull/55444#issuecomment-434580987
- https://github.com/rust-lang/rust/issues/47493