CSIS TechBlog
Published in

CSIS TechBlog

cross compiling and statically linking against Rust libraries

Crustaceans are adaptable and can be found in all kinds of environments.

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.


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)

installed targets for active toolchain


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 :

name = "mylib"
version = "0.1.0"
authors = ["john doe<jdo@csis.dk>"]

libc = "*"

crate-type = ["staticlib"]

Now the source code for our library src/lib.rs:

extern crate libc;
use libc::uint32_t;

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" {
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:


Linux binaries

In order to statically link Linux binaries the target needs to be set to -linux-musl.

  • .cargo/config

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
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 \

If the target was a 64bit environment, x86_64-w64-mingw32-g++ should be used instead and the -L flag adjusted accordingly.


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.



CSIS Security Group software development and security research teams are sharing their experiences building systems to detect, monitor and take down malware infrastructure.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store