对 WebAssembly 目标的更改以及未定义符号的处理

发布: (2026年4月4日 GMT+8 08:00)
5 分钟阅读
原文: Rust Blog

Source: Rust Blog

TL;DR

  • Rust 中的所有 WebAssembly 目标在链接时都使用了传递给 wasm‑ld--allow-undefined 标志。
  • 该标志正被 移除
  • 此更改将在 Rust 1.96(2026‑05‑28)中发布。

什么是 --allow-undefined

Rust 中的 WebAssembly 二进制文件是通过 wasm‑ld(WebAssembly 链接器)进行链接生成的。自从首次引入 WebAssembly 目标以来,链接时一直会向 wasm‑ld 传递以下标志:

--allow-undefined

其文档说明如下:

--allow-undefined       Allow undefined symbols in linked binary.
                         This option is equivalent to
                         --import-undefined and
                         --unresolved-symbols=ignore-all

在此上下文中的 “未定义”

  • 未定义符号 是指链接器在当前对象文件集合中找不到对应定义的符号。
  • 在 Rust 中,你可以通过 extern "C" 块引用这些符号,例如:
unsafe extern "C" {
    fn mylibrary_init();
}

fn init() {
    unsafe {
        mylibrary_init();
    }
}
  • mylibrary_init 在链接时是未定义的,直到另一个组件(例如 C 库)提供了它的实现。

当使用 --allow-undefined 时,链接器会把未定义符号当作 导入 处理,并在生成的模块中输出类似如下内容:

(module
  (import "env" "mylibrary_init" (func $mylibrary_init))
  ;; …
)

因此,未定义符号会被链接器 忽略,并在最终的 WebAssembly 模块中作为导入出现。

为什么会添加这个标志?

wasm‑ld 集成的早期阶段,--allow-undefined 实际上是让链接能够成功的必需选项。这个变通办法一直沿用下来,甚至成为默认行为,尽管现在已经不再需要它。

--allow-undefined 有什么问题?

在所有 WebAssembly 目标上使用 --allow-undefined 会导致 行为分歧,因为在本地平台上未定义的符号默认会报错。主要问题包括:

  1. 静默失败 – 配置错误会生成一个看似可运行的模块,但实际上导入了错误的符号。

  2. 难以诊断的错误 – 消费该模块的工具(例如 wasm-bindgenwasm-tools component new)无法识别默认的 "env" 导入,从而产生令人困惑的提示。

  3. 运行时意外 – 在浏览器中可能会看到类似以下的错误

    Uncaught TypeError: Failed to resolve module specifier "env".
    Relative references must start with either "/", "./", or "../".

    真正的问题是一个未定义的符号泄漏进了模块,而不是缺少 "env" 模块。

所有本地平台都将未定义符号视为错误,因此 Rust 当前在 WebAssembly 上的行为令人惊讶。去掉该标志可以使 WebAssembly 与本地平台保持一致,并为开发者提供更早、更清晰的诊断信息。

什么会被破坏,如何修复?

预期影响

在大多数情况下不会出现破坏——二进制文件如果导入了嵌入环境未提供的符号,只会无法运行。这实际上是一件好事:你会得到链接错误,而不是运行时失败。

对该标志的有意依赖

一些项目有意依赖 --allow-undefined 来生成导入,例如:

unsafe extern "C" {
    fn js_log(n: u32);
}

以及提供该导入的 JavaScript:

let instance = await WebAssembly.instantiate(module, {
  env: {
    js_log: n => console.log(n),
  },
});

修复方案

  1. 显式导入模块 – 使用 #[link] 属性告诉链接器符号所属的模块:

    #[link(wasm_import_module = "env")]
    unsafe extern "C" {
        fn js_log(n: u32);
    }

    这使导入显式化,符号因此不再被视为未定义,且在更改前后都能工作。

  2. 临时恢复旧行为 – 如果需要快速解决方案,手动使用该标志编译:

    cargo rustc -- -Clink-arg=--allow-undefined

    (或在 .cargo/config.toml 中添加 -Clink-arg=--allow-undefined。)

这个更改何时发生?

--allow-undefined 的移除已在 rust-lang/rust#149868 中跟踪。

  • Nightly:此更改即将上线。
  • Stable:它将在 Rust 1.962026‑05‑28 一起发布。

如果在更改后遇到任何问题,请在 Rust 仓库中提交 issue。


敬请关注,祝编码愉快!

0 浏览
Back to Blog

相关文章

阅读更多 »