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/ -lmylibIf 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 \
-luserenvIf 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

