Questions
I have a Rust library that exposes few functions for FFI
. Therefore I must set crate-type to cdylib
.
From where did you get this information? You can create either dynamic library (.so
) or a static library (.a
). Alex has a repository with lot of examples: rust-ffi-examples.
musl is used in situations where you do want to create a binary, which is statically linked and has no dependencies at all (staticlib
). Everything's inside binary. You throw it at the Linux box and it will work.
Dynamic linking is used in situations where you know that all the dependencies will be met, you want smaller binaries, etc. (cdylib
). But, you have to be sure that the dependencies are really there otherwise it won't work.
Cross compilation
I usually don't bother with cross compilation, because it can be very tricky if you need to dynamically link against other Linux libraries. For these cases, I've got:
- Linux installed in the VMware Fusion,
- Docker for Mac installed with Linux image inside.
There're many ways how to achieve what you want. See @Shepmaster comment: use CI and upload build artifacts somewhere.
Do you really need cross compilation? Isn't there any other way how to achieve your goal? Avoid it when possible.
Dymamic library
Toolchain
$ brew tap SergioBenitez/osxct
$ brew install x86_64-unknown-linux-gnu
$ rustup target add x86_64-unknown-linux-gnu
Add following lines to the ~/.cargo/config
:
[target.x86_64-unknown-linux-gnu]
linker = "x86_64-unknown-linux-gnu-gcc"
Sample Rust library
Cargo.toml
content:
[package]
name = "sample"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib"]
src/lib.rs
content:
#[no_mangle]
pub extern fn hello() {
println!("Rust here");
}
Compile & check
Compile with:
$ cargo build --release --target x86_64-unknown-linux-gnu
Check the output:
$ file target/x86_64-unknown-linux-gnu/release/libsample.so
target/x86_64-unknown-linux-gnu/release/libsample.so: ELF 64-bit LSB pie executable x86-64, version 1 (SYSV), dynamically linked, with debug_info, not stripped
Check library symbols:
x86_64-unknown-linux-gnu-nm -D target/x86_64-unknown-linux-gnu/release/libsample.so | grep hello
0000000000003900 T hello
Test on the Linux box
Copy target/x86_64-unknown-linux-gnu/release/libsample.so
to your Linux box.
Manual loading
sample.c
content:
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
int main(int argc, char**argv) {
void *lib;
void (*hello)(void);
char *error;
lib = dlopen("./libsample.so", RTLD_NOW);
if (!lib) {
fprintf(stderr, "%s\n", dlerror());
exit(-1);
}
dlerror();
*(void **)(&hello) = dlsym(lib, "hello");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "%s\n", error);
dlclose(lib);
exit(-1);
}
(*hello)();
dlclose(lib);
exit(0);
}
Compile with gcc -rdynamic -o sample sample.c -ldl
and run it:
$ ./sample
Rust here
Check that it's dynamically linked:
$ ldd ./sample
linux-vdso.so.1 (0x00007ffe609eb000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc7bdd69000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc7bd978000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc7be16f000)
Dynamic linking
sample.c
content:
extern void hello(void);
int main(int argc, char **argv) {
hello();
}
Compile with gcc sample.c -o sample -lsample -L.
and run it:
$ LD_LIBRARY_PATH=. ./sample
Rust here
Check that it's dynamically linked:
$ LD_LIBRARY_PATH=. ldd ./sample
linux-vdso.so.1 (0x00007ffc6fef6000)
libsample.so => ./libsample.so (0x00007f8601ba3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f86017b2000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f86015ae000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f86013a6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8601187000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8600f6f000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8601fd5000)
Static library
Toolchain
$ rustup target add x86_64-unknown-linux-musl
$ brew install filosottile/musl-cross/musl-cross
Add following lines to the ~/.cargo/config
:
[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"
Sample Rust library
Cargo.toml
content:
[package]
name = "sample"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["staticlib"]
src/lib.rs
content:
#![crate_type = "staticlib"]
#[no_mangle]
pub extern fn hello() {
println!("Rust here");
}
Compile
Compile with:
$ cargo build --release --target x86_64-unknown-linux-musl
Test on the Linux box
Copy target/x86_64-unknown-linux-musl/release/libsample.a
to your Linux box.
sample.c
content:
extern void hello(void);
int main(int argc, char **argv) {
hello();
}
Compile with gcc sample.c libsample.a -o sample
and run it:
$ ./sample
Rust here
Check that it's statically linked:
$ ldd ./sample
statically linked