Linking Static Linux Libraries in Rust
I had a hell of a time getting this to work. Here’s how I did it. Let’s write a small library in C++.
// mysum.h
#ifdef __cplusplus
// extern "C" is needed so that C++ doesn't mangle the mysum symbol into something like _Z5mysumii
extern "C"
{
#endif
int mysum(int i, int j);
#ifdef __cplusplus
}
#endif
// mysum.cpp
include "mysum.h"
int mysum(int i, int j)
{
return i + j;
{
In Linux, we can compile them with g++ into an object file then package them in a static library with ar.
g++ -c mysum.cpp -o mysum.o ar rcs mysum.a mysum.o
Create a rust project named libtest.
cargo init libtest
Create a directory libs inside libtest, and copy the mysum.a file as libmysum.a into libtest. The header doesn’t need to be included for this example.
I’m going to repeat that: mysum.a needs to be named libmysum.a inside libtest/libs.
Inside libtest, create a build.rs file and edit it to look like this:
fn main() {
println!("cargo:rustc-link-lib=static=stdc++");
println!("cargo:rustc-link-lib=mysum");
println!("cargo:rustc-link-search=native=./libs");
}
Notice that cargo:rustc-link-lib=mysum lacks the lib prefix? It’s strange.
Add a line to the [package] section in Cargo.toml file to include the line build = “build.rs”
[package] name = "libtest" version = "0.1.0" edition = "2021" build = "build.rs"
Finally, it’s time to edit src/main.rs:
#[link(name="mysum")]
extern "C" {
fn mysum(i: i32, j: i32) -> i32;
}
fn main() {
println!("Hello, world!");
let result = unsafe {
mysum(5, 6)
};
println!("Result: {}", result);
}
Notice, again, that the library is named libmysum.a, but it’s referred to as just mysum. Very strange, indeed.
cargo run ... Hello, world! Result: 11
Okay. It’s time for some THAT WAY MADNESS LIES.
Earlier, we were careful to wrap all of our C++ functions inside an extern “C” block. This limits us. Maybe we have a static library built for C++ which cannot be altered. Maybe it has overloaded functions. Who knows? Let’s imagine that we have this kind of a library.
// multisum.h int multisum(int i, int j); int multisum(int i, int j, int k);
// multisum.cpp
// notice no extern "C" block
#include "multisum.h"
int multisum(int i, int j)
{
return i + j;
}
int multisum(int i, int j, int k)
{
return i + j + k;
}
Let’s compile it into an object file and pack it as above. I’ll pretend that it’s already inside libtest/libs as libmultisum.a, but what will we do about the function overloading? Rust doesn’t let you do that! Behold, and despair!
nm libmultisum.a multisum.o: 0000000000000000 T _Z8multisumii 0000000000000018 T _Z8multisumiii
You better believe that those are the symbol names for the functions. Let’s get all of our ducks in a row. build.rs:
fn main() {
println!("cargo:rustc-link-lib=static=stdc++");
println!("cargo:rustc-link-lib=multisum");
println!("cargo:rustc-link-search=native=./libs");
}
src/main.rs:
#[link(name="multisum")]
extern "C" {
fn _Z8multisumii(i: i32, j: i32) -> i32;
fn _Z8multisumiii(i: i32, j: i32, k: i32) -> i32;
}
fn main() {
println!("Hello, world!");
let result1 = unsafe {
_Z8multisumii(5, 6)
};
let result2 = unsafe {
_Z8multisumiii(5, 6, 7)
};
println!("2-number result: {}", result1);
println!("3-number result: {}", result2);
}
cargo run ... Hello, world! 2-number result: 11 3-number result: 18