Skip to content

scristobal/cross-compiling-rust-c-wasm-zig

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crosscompile a Rust project with C dependencies into Web Assembly (WASM and WASI) using Zig

Cross-compiling made easy way using cargo-zigbuild CLI.

Compile to WebAssembly (wasi)

rustup target add wasm32-wasip1 # make sure wasm32-wasi target is installed 
cargo zigbuild --target=wasm32-wasip1 --release # cross compile to WASI, release flag is optional

Warning

Previously the target wasm32-wasip1 was wasm32-wasi but it is now being deprecated, still you might want to use it even if you get some warnings.

we can try it with wasm3 engine

wasm3 target/wasm32-wasip1/release/rust-ffi-playground.wasm

or wasmi

wasmi_cli target/wasm32-wasip1/release/rust-ffi-playground.wasm

Cross compile for WebAssembly (web)

Generate code for WASM with zigbuild, and then use wasm-bindgen to generate the js/ts bindings to the WASM code.

cargo zigbuild --target=wasm32-unknown-unknown --release # cross compile to WASM, release flag is optional
wasm-bindgen target/wasm32-unknown-unknown/release/rust-ffi-playground.wasm --out-dir ./dist --target web # generate JS and TS FFI bindings into WASM code

To try it, manually include the script tag to load and initialize the wasm module

<script type="module">
  import init from "./dist/rust-ffi-playground.js";
  init().then(() => console.log("WASM Loaded"));
</script>

or use a WASM plugin like vite-plugin-wasm or use Trunk

Note: As in this github issue it can be compiled to wasm32-unknown-emscripten.

Same example but using wasm-pack, hence wasm-bindgen instead

Going further

Generate Rust bindings for the C code

The Rust FFI bindings to the C function are generated at build time in build.rs but they can also be generated manually

#[repr(C)]
pub struct Pair {
    pub n: ::core::ffi::c_int,
    pub m: ::core::ffi::c_int,
}

unsafe extern "C" {
    pub fn gcd(ps: *mut Pair) -> ::core::ffi::c_int;
}

or using rust-bindgen CLI.

bindgen some-c-code/gcd.h -o src/bindings.rs

Cross compilation without zigbuild

Without cargo-zigbuild, you need to manually install cross-compilation toolchains and configure Cargo to use them.

WebAssembly (wasm32-wasip1)

Use clang/LLVM to compile C code to WASM:

# Install WASI SDK (provides clang with WASI sysroot)
# Download from https://github.com/WebAssembly/wasi-sdk/releases

export WASI_SDK_PATH=/path/to/wasi-sdk
export CC_wasm32_wasip1="$WASI_SDK_PATH/bin/clang --sysroot=$WASI_SDK_PATH/share/wasi-sysroot"
export AR_wasm32_wasip1="$WASI_SDK_PATH/bin/llvm-ar"

rustup target add wasm32-wasip1
cargo build --target=wasm32-wasip1 --release

WebAssembly (wasm32-unknown-unknown)

export CC_wasm32_unknown_unknown="clang --target=wasm32"
export AR_wasm32_unknown_unknown="llvm-ar"

rustup target add wasm32-unknown-unknown
cargo build --target=wasm32-unknown-unknown --release

References

Issues

wasm-bindgen targets wasm32-unknown-unknown and wasi-unknown do not (fully) support C-ABI, only older targets like wasm32-unknown-emscripten.

See comment, comment and documentation PR

There is an experimental flag --Z wasm_c_abi=spec that circumvents this limitation

Update: it has been merged!

Other tools

  • c2rust - C to Rust translator produces unsafe Rust code from C99-compilant C code. It does not support cross compilation, but maybe it can with the help of Zig.

From their website:

C source code is parsed and typechecked using clang before being translated by our tool.

would it be possible to use it with Zig as a drop-in replacement for clang?

From their README:

I translated code on platform X, but it didn't work correctly on platform Y. We run the C preprocessor before translation to Rust. This specializes the code to the host platform. For this reason, we do not support cross compiling translated code at the moment. What platforms can C2Rust be run on? The translator and refactoring tool support both macOS and Linux. Other features, such as cross checking the functionality between C and Rust code, are currently limited to Linux hosts.

Utils


Remarks

In general, a lib<name>.so or lib<name>.a should be referenced in the build file by <name>.

About

Cross compiling Rust + C to WebAssembly using Zig

Topics

Resources

Stars

Watchers

Forks

Contributors