Changes to WebAssembly targets and handling undefined symbols
Rust's WebAssembly targets are soon going to experience a change which has a risk of breaking existing projects, and this post is intended to notify users of this upcoming change, explain what it is, and how to handle it. Specifically, all WebAssembly targets in Rust have been linked using the --allow-undefined flag to wasm-ld , and this flag is being removed. What is --allow-undefined ? WebAssembly binaries in Rust today are all created by linking with wasm-ld . This serves a similar purpose to ld , lld , and mold , for example; it takes separately compiled crates/object files and creates one final binary. Since the first introduction of WebAssembly targets in Rust, the --allow-undefined flag has been passed to wasm-ld . This flag is documented as: --allow-undefined Allow undefined symbols in linked binary. This options is equivalent to --import-undefined and --unresolved-symbols=ignore-all The term "undefined" here specifically means with respect to symbol resolution in wasm-ld itself. Symbols used by wasm-ld correspond relatively closely to what native platforms use, for example all Rust functions have a symbol associated with them. Symbols can be referred to in Rust through extern "C" blocks, for example: unsafe extern " C " { fn mylibrary_init ( ) ; } fn init ( ) { unsafe { mylibrary_init ( ) ; } } The symbol mylibrary_init is an undefined symbol. This is typically defined by a separate component of a program, such as an externally compiled C library, which will provide a definition for this symbol.

Rust's WebAssembly (Wasm) targets are set to undergo a significant change that may impact existing projects. This article aims to inform users about the upcoming modification, explain its implications, and provide guidance on how to adapt to it.
For years, Rust's WebAssembly binaries have been compiled using the `--allow-undefined` flag with `wasm-ld`. This flag, which allows undefined symbols in the linked binary, has been a staple in the Rust ecosystem since the inception of WebAssembly support. However, the Rust team is now planning to remove this flag, and developers must prepare their projects accordingly.
To understand the significance of this change, it's essential to grasp what `--allow-undefined` does. `wasm-ld` serves a similar purpose as traditional linkers like `ld`, `lld`, or `mold`; it combines separately compiled Rust crates and object files into a single WebAssembly binary. The `--allow-undefined` flag instructs `wasm-ld` to ignore undefined symbols during the linking process.
In Rust, symbols are often referenced through `extern "C"` blocks. For example:
```rust
unsafe extern "C" {
fn mylibrary_init() -> ();
}
fn init() {
unsafe {
mylibrary_init();
}
}
```
Here, `mylibrary_init` is an undefined symbol. Such symbols are typically defined by external components, such as C libraries, which provide the actual implementation.
By using `--allow-undefined`, the linker ignores these undefined symbols and treats them as imported symbols in the final WebAssembly module. This results in a module like:
```wat
(module
(import "env" "mylibrary_init" (func $mylibrary_init))
;; ...
)
```
The exact history of this flag's inclusion is unclear, but it has been a part of Rust's WebAssembly toolchain for an extended period.
The upcoming change involves removing the `--allow-undefined` flag. This means that Rust projects relying on this flag may encounter errors when linking, as undefined symbols will no longer be ignored. Developers must ensure that all symbols referenced in their code are properly defined.
To adapt to this change, developers should audit their projects for any undefined symbols. This can be done by compiling their code with the `--warn` flag, which will issue warnings for any undefined symbols. Alternatively, they can use the `wasm-ld` `--unresolved-symbols=error` flag to generate errors for undefined symbols during the linking phase.
In many cases, the undefined symbols in question are imported from external libraries or the WebAssembly runtime environment. If these symbols are indeed defined elsewhere, no action is required. However, if a symbol is truly undefined and not imported, developers must provide a definition for it.
For symbols defined in Rust code, this can be achieved by implementing the corresponding function or ensuring that the necessary crate is included in the project's dependencies. If the symbol is defined in a C library, the developer must ensure that the library is correctly compiled and linked against the Rust code.
In some instances, the undefined symbol may be part of the WebAssembly runtime environment. In such cases, the symbol is typically predefined, and no action is needed. However, if the symbol is not predefined, the developer must provide a definition or import it from an external source.
In conclusion, the removal of the `--allow-undefined` flag in Rust's WebAssembly targets poses a risk to existing projects. Developers must carefully examine their codebases to identify and resolve any undefined symbols. By understanding the role of `--allow-undefined` and adopting best practices for symbol resolution, Rust developers can ensure their projects remain compatible with the upcoming change. As always, thorough testing and careful code review are crucial in navigating this transition smoothly.










