Skip to content

cabi_realloc is referenced but never linked on wasm32-unknown-emscripten #1751

Open

Description

@WilliamKarolDiCioccio

crates/guest-rust/build.rs (still the case in v0.63.0) decides whether to link the cabi_realloc shim with:

let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or(String::new());
if target_family != "wasm" {
    return;
}

Cargo joins a cfg with several values with commas. wasm32-unknown-emscripten is the one wasm target with two families, so CARGO_CFG_TARGET_FAMILY is unix,wasm there, the comparison fails, and the build script returns before linking anything.

The crate's own Rust cfgs disagree with it. In src/rt/mod.rs, maybe_link_cabi_realloc references cabi_realloc under #[cfg(all(target_family = "wasm", any(target_env = "p1", target_env = "")))], and cfg(target_family = "wasm") is true on Emscripten, whose target_env is "". So the symbol is referenced but never provided. Any static library built on wit-bindgen for Emscripten then fails its final emcc link.

Reproduction (wit-bindgen 0.63.0, Rust 1.98.0, Emscripten 6.0.12):

# Cargo.toml
[lib]
crate-type = ["staticlib"]
[dependencies]
wit-bindgen = "=0.63.0"
// src/lib.rs, with wit/w.wit = `package repro:w; world w { export greet: func(name: string) -> string; }`
wit_bindgen::generate!({ world: "w", path: "wit" });
struct C;
impl Guest for C { fn greet(name: String) -> String { format!("hi {name}") } }
export!(C);
#[unsafe(no_mangle)]
pub extern "C" fn answer() -> i32 { 42 }
cargo build --release --target wasm32-unknown-emscripten
emcc main.c target/wasm32-unknown-emscripten/release/libwbrepro.a -o m.js
# wasm-ld: error: …wit_bindgen….rcgu.o: undefined symbol: cabi_realloc

(main.c calls answer().)

Fix: read the family as a list.

if !target_family.split(',').any(|f| f == "wasm") {
    return;
}

With this patched in through [patch.crates-io], the reproduction links and runs under node. Builds for wasm32-unknown-unknown, wasm32-wasip2 and x86_64-unknown-linux-gnu are unaffected; Emscripten is the only target whose family list differs.

The check dates from #851, where it reads as "any wasm target", with a panic for wasm architectures other than wasm32. I found nothing suggesting Emscripten was meant to be excluded. If it was, the cfgs in rt/mod.rs would need to exclude it too, so as it stands the two halves disagree either way.

Context: we hit this building a C ABI whose types come from wit-bindgen-generated bindings, for hosts that compile to the browser with Emscripten. We work around it with a #[no_mangle] extern "C" fn cabi_realloc on Emscripten that forwards to wit_bindgen::rt::cabi_realloc.

Activity

  1. alexcrichton commented on Oct 15, 2026

    @alexcrichton
    Member

    This crate hasn't been designed so far to support Emscripten yet so I'm not entirely surprised that there are problems. Happy to take a PR to work on this, however.

  2. WilliamKarolDiCioccio commented on Oct 19, 2026

    @WilliamKarolDiCioccio
    Author

    Hi Alex,

    Thank you for your answer. I'm aware this crate wasn't designed to support Emscripten but I surfaced the problem anyway so that you could make the call on whether this was worth addressing or not. I guess you're really busy so if you want I can take on this. Let me know.

  3. alexcrichton commented on Oct 19, 2026

    @alexcrichton
    Member

    Yes if you're willing to send a PR that'd be much appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions