Skip to content

rafaelbeckel/test-c-rust-wasm

Repository files navigation

Rust+C/C++ in the same WASM binary

A workspace of progressive examples showing how to produce a single WASM binary that combines Rust, C, and C++ code — from manual linking all the way to integrating real external libraries with a full C/C++ standard library.

Context

As of Rust v1.89 (August 2025), the wasm32-unknown-unknown target officially uses the standards-compliant C ABI by default. This means C and Rust code compiled to WASM can call each other without any special flags.

For historical context, see the relevant tracking issue in wasm-bindgen and the official Rust blog post about this change.

Build Strategies

The crates experiment with different build strategies, with increasing levels of complexity:

Minimal Examples (no external dependencies)

  1. Linking Manually — A simple calculator with primitive data types and manual build using wasm-ld.

  2. With CC Crate — The same calculator built with the CC crate.

With Musl Libc (require ./setup.sh)

All crates from 3 onwards use musl libc (v1.2.6) compiled for wasm32-unknown-unknown, providing a full C standard library (malloc/free, printf/snprintf, string operations, etc.).

  1. Libc and Heap — Adds heap allocation (malloc/free) via musl libc.

  2. Wasm Bindgen — We create a Calculator struct with member functions and export it with wasm-bindgen.

  3. Rust Bindgen — Same as above, but we generate the Rust bindings from the C header with Rust Bindgen.

  4. Extern Types — Experiments with the nightly feature extern types.

  5. Musl Libc (full demo) — A more complete calculator that exercises musl's snprintf for formatted output. Demonstrates the pattern used in production for integrating real-world C libraries.

  6. LLVM Libc++ — Adds LLVM's libc++ on top of musl libc, enabling C++ standard library features (std::vector, std::string, <algorithm>, etc.) in WASM. The C++ Calculator class demonstrates how to wrap C++ objects with a C API for Rust FFI. Build configuration mirrors emscripten's system_libs.py.

  7. Capstone: zlib — Integrates a real, unmodified external C library (zlib) compiled from source. Demonstrates the full production pattern: external C library + musl libc + safe Rust wrappers + wasm-bindgen export.

Setup

Crates 1–2 (minimal)

No setup required. Just build:

cd crates/1_linking_manually && ./build.sh

Crates 3–9 (require submodules)

Initialize the git submodules first, then build:

# Initialize submodules (musl, emscripten libcxx, zlib)
./setup.sh

# Build everything
./build_all.sh

# Or build a specific crate
cd crates/4_wasm_bindgen && ./build.sh

Dependencies

All examples require LLVM, Clang, Rust (stable), and wasm-pack. Crate 6 (extern_types) additionally needs the nightly toolchain — its own rust-toolchain.toml selects it automatically when you cd into that crate.

For inspecting outputs: Wasm Binary Toolkit (wasm2wat).

Running

To see the examples in action, use your favorite local server:

npx serve

Then visit http://localhost:3000 and click the example you want to see.

Architecture

How the C/C++ standard libraries work

The 3rd-party/ directory contains two Rust crates that compile and link the C/C++ standard libraries for wasm32-unknown-unknown:

3rd-party/
├── wasm32-libc/         # Musl libc compiled for wasm32
│   ├── musl/            # ← git submodule (https://git.musl-libc.org/cgit/musl)
│   ├── src/ffi/         # Rust implementations of C stdlib functions
│   └── build.rs         # Compiles musl + generates Rust bindings
│
└── wasm32-libcxx/       # LLVM libc++ compiled for wasm32
    ├── emscripten/      # ← git submodule (https://github.com/emscripten-core/emscripten)
    └── build.rs         # Compiles libcxx + libcxxabi from emscripten sources

The wasm32-libcxx crate depends on wasm32-libc (C++ needs a C library underneath). Consumer crates declare these as Cargo dependencies, and header paths are propagated via Cargo's DEP_*_INCLUDE environment variables.

Dependency chain for crate 9

zlib_compression (Rust crate)
├── zlib (external C library, compiled from source)
├── wasm32-libc (musl libc for wasm32)
└── wasm-bindgen (JS interop)

Contributing

If you'd like to see any other scenario listed here, feel free to open an Issue or a PR.

If submitting a new example, create a numbered subfolder in the crates directory following the existing structure, and ensure your example builds correctly for both WASM and unit tests.

Finally, run cargo clippy and stick with the default rules. CI runs cargo fmt --check, cargo clippy -D warnings, cargo test, and ./build_all.sh on every pull request — see .github/workflows/ci.yml.

About

Rust, C, and C++ WASM compilation under the same binary for the `wasm32-unknown-unknown` target

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors