Updates

Rust Compiler Updates WebAssembly Targets, Changing Default Features and Symbol Handling

Rust 1.96 removes allow-undefined from all Wasm linker calls; builds silently papering over missing symbols today will fail to link on May 28.

Jamie Taylor2 min read
Published
Listen to this article0:00 min
Share this article:
Rust Compiler Updates WebAssembly Targets, Changing Default Features and Symbol Handling
AI-generated illustration
This article contains affiliate links, marked with a blue dot. We may earn a small commission at no extra cost to you.

A Wasm build that compiles and runs perfectly today may throw a hard linker error starting May 28. That is the core message from the Rust project's April 4 blog post, co-authored by Alex Crichton and Kevin Reid, announcing the removal of the ` allow-undefined` flag from every WebAssembly target in rustc.

The ` allow-undefined` flag has been passed to `wasm-ld`, Rust's WebAssembly linker, since Wasm targets were first introduced. The original rationale was practical: in the early days of `wasm-ld` integration, the flag was reportedly necessary to import host functions into a binary at all. That constraint is long gone, but the flag stayed, quietly turning every undefined symbol into a generated import rather than a build error. The result is behavior that diverges from every other platform Rust targets, where an undefined symbol is simply a compile-time failure.

The divergence has real consequences. Consider a bare `unsafe extern "C" { fn foo(); }` block with no `wasm_import_module` annotation. Under the current toolchain, that code compiles, links, and ships a Wasm binary that imports `foo` from the `env` module, perhaps not what the author intended. Worse, a typo like writing `mylibraryinit` when the linked C symbol is `mylibrary_init` produces a binary that cheerfully imports the misspelled name and explodes at runtime instead of failing at link time. A reference to a nonexistent static, `static nonexistent: u8`, currently compiles and runs, printing null rather than surfacing any error.

After the change tracked in PR #149868, all three of those examples produce a linker error. The `foo` symbol is not defined; the typo'd import name is not defined; the phantom static is not defined. The build fails where the problem was introduced, not somewhere downstream at runtime.

The fix for code that genuinely does import from a host module is straightforward: add the `wasm_import_module` attribute. A block written as `#[link(wasm_import_module = "host")] unsafe extern "C" { fn foo(); }` continues to work exactly as before, both before and after the change. That annotation tells `wasm-ld` exactly which host module provides the symbol, so no undefined-symbol error is raised. For projects that need a quick escape hatch while sorting out attribution, passing `-Clink-arg= allow-undefined` restores the old behavior without any source changes, though that is intended as a temporary measure.

Tooling authors using wasm-bindgen or wasm-pack should audit any generated or hand-written extern blocks on `wasm32-unknown-unknown` and WASI targets. Bundlers and runtime providers that rely on implicit imports from the `env` module may surface new linker errors once their upstream dependencies update. The blog post specifically notes that filing issues against rust-lang/rust is the right path if unexpected fallout surfaces after the flag drops.

The change lands in nightly shortly after the announcement and ships in Rust 1.96 on May 28, 2026. Running a Wasm build against nightly now is the fastest way to find out whether any crate in a dependency tree was quietly relying on ` allow-undefined` to mask a symbol problem that was always there.

Know something we missed? Have a correction or additional information?

Submit a Tip

Discussion

More Rust Programming News