惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
F
Full Disclosure
雷峰网
雷峰网
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
量子位
Microsoft Azure Blog
Microsoft Azure Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
人人都是产品经理
人人都是产品经理
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
Help Net Security
Help Net Security
腾讯CDC
I
InfoQ
Know Your Adversary
Know Your Adversary
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
Check Point Blog
W
WeLiveSecurity
NISL@THU
NISL@THU
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
N
Netflix TechBlog - Medium
月光博客
月光博客
Schneier on Security
Schneier on Security
Forbes - Security
Forbes - Security
Project Zero
Project Zero
宝玉的分享
宝玉的分享
P
Palo Alto Networks Blog
P
Privacy & Cybersecurity Law Blog
罗磊的独立博客
Latest news
Latest news
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
Stack Overflow Blog
Stack Overflow Blog
S
Security Affairs
I
Intezer
爱范儿
爱范儿
Microsoft Security Blog
Microsoft Security Blog
A
Arctic Wolf
有赞技术团队
有赞技术团队
云风的 BLOG
云风的 BLOG
T
Tenable Blog
Hacker News: Ask HN
Hacker News: Ask HN
L
LINUX DO - 最新话题
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

Rust Blog

Security Advisory for Cargo (CVE-2026-5223) | Rust Blog Security Advisory for Cargo (CVE-2026-5222) | Rust Blog Project goals update — April 2026 (end of 2025H2) | Rust Blog Rust is participating in Outreachy | Rust Blog Raising the baseline for the `nvptx64-nvidia-cuda` target | Rust Blog Announcing Google Summer of Code 2026 selected projects | Rust Blog Announcing Rust 1.95.0 | Rust Blog docs.rs: building fewer targets by default | Rust Blog Changes to WebAssembly targets and handling undefined symbols | Rust Blog Announcing Rust 1.94.1 | Rust Blog Security advisory for Cargo | Rust Blog What we heard about Rust's challenges | Rust Blog Call for Testing: Build Dir Layout v2 | Rust Blog Announcing rustup 1.29.0 | Rust Blog Announcing Rust 1.94.0 | Rust Blog 2025 State of Rust Survey Results | Rust Blog Rust debugging survey 2026 | Rust Blog Update on the October 15, 2018 incident on crates.io Announcing Rust 1.29.2 Announcing Rust 1.29 Announcing Rust 1.28 What is Rust 2018? Announcing Rust 1.27.2 Announcing Rust 1.27.1 Security Advisory for rustdoc Announcing Rust 1.27 Announcing Rust 1.26.2 Announcing Rust 1.26.1 Rust turns three Announcing Rust 1.26 The Rust Team All Hands in Berlin: a Recap Increasing Rust’s Reach 2018 Announcing Rust 1.25 Rust's 2018 roadmap Announcing Rust 1.24.1 Announcing Rust 1.24 The 2018 Rust Event Lineup Announcing Rust 1.23 New Year's Rust: A Call for Community Blogposts Rust in 2017: what we achieved Announcing Rust 1.22 (and 1.22.1) Fearless Concurrency in Firefox Quantum Announcing Rust 1.21 impl Future for Rust Rust 2017 Survey Results Announcing Rust 1.20 Announcing Rust 1.19 The 2017 Rust Conference Lineup Rust's 2017 roadmap, six months in Increasing Rust’s Reach Announcing Rust 1.18 Two years of Rust The Rust Libz Blitz Launching the 2017 State of Rust Survey Announcing Rust 1.17 Announcing Rust 1.16 Rust's language ergonomics initiative Announcing Rust 1.15.1 Rust's 2017 roadmap Announcing Rust 1.15 Announcing Rust 1.14 Announcing the First Underhanded Rust Contest Announcing Rust 1.13 Announcing Rust 1.12.1 Announcing Rust 1.12 Incremental Compilation Announcing Rust 1.11 Shape of errors to come The 2016 Rust Conference Lineup Announcing Rust 1.10 State of Rust Survey 2016 Announcing Rust 1.9 One year of Rust Taking Rust everywhere with rustup Launching the 2016 State of Rust Survey Cargo: predictable dependency management Introducing MIR Announcing Rust 1.8 Announcing Rust 1.7 Announcing Rust 1.6 Announcing Rust 1.5 Announcing Rust 1.4 Announcing Rust 1.3 Rust in 2016 Announcing Rust 1.2 Rust 1.1 stable, the Community Subteam, and RustCamp Announcing Rust 1.0 Abstraction without overhead: traits in Rust Rust Once, Run Everywhere Mixing matching, mutation, and moves in Rust Fearless Concurrency with Rust Announcing Rust 1.0 Beta Announcing Rust 1.0.0.alpha.2 Rust 1.0: status report and final timeline Announcing Rust 1.0 Alpha Rust 1.0: Scheduling the trains Yehuda Katz and Steve Klabnik are joining the Rust Core Team Cargo: Rust's community crate host Stability as a Deliverable Road to Rust 1.0
C ABI Changes for `wasm32-unknown-unknown`
Alex Crichton · 2025-04-04 · via Rust Blog

The extern "C" ABI for the wasm32-unknown-unknown target has been using a non-standard definition since the inception of the target in that it does not implement the official C ABI of WebAssembly and it additionally leaks internal compiler implementation details of both the Rust compiler and LLVM. This will change in a future version of the Rust compiler and the official C ABI will be used instead.

This post details some history behind this change and the rationale for why it's being announced here, but you can skip straight to "Am I affected?" as well.

History of wasm32-unknown-unknown's C ABI

When the wasm32-unknown-unknown target was originally added in 2017, not much care was given to the exact definition of the extern "C" ABI at the time. In 2018 an ABI definition was added just for wasm and the target is still using this definition to this day. This definitions has become more and more problematic over time and while some issues have been fixed, the root cause still remains.

Notably this ABI definition does not match the tool-conventions definition of the C API, which is the current standard for how WebAssembly toolchains should talk to one another. Originally this non-standard definition was used for all WebAssembly based targets except Emscripten, but this changed in 2021 where the WASI targets for Rust use a corrected ABI definition. Still, however, the non-standard definition remained in use for wasm32-unknown-unknown.

The time has now come to correct this historical mistake and the Rust compiler will soon be using a correct ABI definition for the wasm32-unknown-unknown target. This means, however, that generated WebAssembly binaries will be different than before.

What is a WebAssembly C ABI?

The definition of an ABI answers questions along the lines of:

  • What registers are arguments passed in?
  • What registers are results passed in?
  • How is a 128-bit integers passed as an argument?
  • How is a union passed as a return value?
  • When are parameters passed through memory instead of registers?
  • What is the size and alignment of a type in memory?

For WebAssembly these answers are a little different than native platforms. For example, WebAssembly does not have physical registers and functions must all be annotated with a type. What WebAssembly does have is types such as i32, i64, f32, and f64. This means that for WebAssembly an ABI needs to define how to represent values in these types.

This is where the tool-conventions document comes in. That document provides a definition for how to represent primitives in C in the WebAssembly format, and additionally how function signatures in C are mapped to function signatures in WebAssembly. For example a Rust u32 is represented by a WebAssembly i32 and is passed directly as a parameter as a function argument. If the Rust structure #[repr(C)] struct Pair(f32, f64) is returned from a function then a return pointer is used which must have alignment 8 and size of 16 bytes.

In essence, the WebAssembly C ABI is acting as a bridge between C's type system and the WebAssembly type system. This includes details such as in-memory layouts and translations of a C function signature to a WebAssembly function signature.

How is wasm32-unknown-unknown non-standard?

Despite the ABI definition today being non-standard, many aspects of it are still the same as what tool-conventions specifies. For example, size/alignment of types is the same as it is in C. The main difference is how function signatures are calculated. An example (where you can follow along on godbolt) is:

#[repr(C)]
pub struct Pair {
    x: u32,
    y: u32,
}

#[unsafe(no_mangle)]
pub extern "C" fn pair_add(pair: Pair) -> u32 {
    pair.x + pair.y
}

This will generate the following WebAssembly function:

(func $pair_add (param i32 i32) (result i32)
  local.get 1
  local.get 0
  i32.add
)

Notably you can see here that the struct Pair was "splatted" into its two components so the actual $pair_add function takes two arguments, the x and y fields. The tool-conventions, however specifically says that "other struct[s] or union[s]" are passed indirectly, notably through memory. We can see this by compiling this C code:

struct Pair {
    unsigned x;
    unsigned y;
};

unsigned pair_add(struct Pair pair) {
    return pair.x + pair.y;
}

which yields the generated function:

(func (param i32) (result i32)
  local.get 0
  i32.load offset=4
  local.get 0
  i32.load
  i32.add
)

Here we can see, sure enough, that pair is passed in linear memory and this function only has a single argument, not two. This argument is a pointer into linear memory which stores the x and y fields.

The Diplomat project has compiled a much more comprehensive overview than this and it's recommended to check that out if you're curious for an even deeper dive.

Why hasn't this been fixed long ago already?

For wasm32-unknown-unknown it was well-known at the time in 2021 when WASI's ABI was updated that the ABI was non-standard. Why then has the ABI not been fixed like with WASI? The main reason originally for this was the wasm-bindgen project.

In wasm-bindgen the goal is to make it easy to integrate Rust into a web browser with WebAssembly. JavaScript is used to interact with host APIs and the Rust module itself. Naturally, this communication touches on a lot of ABI details! The problem was that wasm-bindgen relied on the above example, specifically having Pair "splatted" across arguments instead of passed indirectly. The generated JS wouldn't work correctly if the argument was passed in-memory.

At the time this was discovered it was found to be significantly difficult to fix wasm-bindgen to not rely on this splatting behavior. At the time it also wasn't thought to be a widespread issue nor was it costly for the compiler to have a non-standard ABI. Over the years though the pressure has mounted. The Rust compiler is carrying an ever-growing list of hacks to work around the non-standard C ABI on wasm32-unknown-unknown. Additionally more projects have started to rely on this "splatting" behavior and the risk has gotten greater that there are more unknown projects relying on the non-standard behavior.

In late 2023 the wasm-bindgen project fixed bindings generation to be unaffected by the transition to the standard definition of extern "C". In the following months a future-incompat lint was added to rustc to specifically migrate users of old wasm-bindgen versions to a "fixed" version. This was in anticipation of changing the ABI of wasm32-unknown-unknown once enough time had passed. Since early 2025 users of old wasm-bindgen versions will now receive a hard error asking them to upgrade.

Despite all this heroic effort done by contributors, however, it has now come to light that there are more projects than wasm-bindgen relying on this non-standard ABI definition. Consequently this blog post is intended to serve as a notice to other users on wasm32-unknown-unknown that the ABI break is upcoming and projects may need to be changed.

Am I affected?

If you don't use the wasm32-unknown-unknown target, you are not affected by this change. If you don't use extern "C" on the wasm32-unknown-unknown target, you are also not affected. If you fall into this bucket, however, you may be affected!

To determine the impact to your project there are a few tools at your disposal:

  • A new future-incompat warning has been added to the Rust compiler which will issue a warning if it detects a signature that will change when the ABI is changed.
  • In 2023 a -Zwasm-c-abi=(legacy|spec) flag was added to the Rust compiler. This defaults to -Zwasm-c-abi=legacy, the non-standard definition. Code can use -Zwasm-c-abi=spec to use the standard definition of the C ABI for a crate to test out if changes work.

The best way to test your crate is to compile with nightly-2025-03-27 or later, ensure there are no warnings, and then test your project still works with -Zwasm-c-abi=spec. If all that passes then you're good to go and the upcoming change to the C ABI will not affect your project.

I'm affected, now what?

So you're using wasm32-unknown-unknown, you're using extern "C", and the nightly compiler is giving you warnings. Additionally your project is broken when compiled with -Zwasm-c-abi=spec. What now?

At this time this will unfortunately be a somewhat rough transition period for you. There are a few options at your disposal but they all have their downsides:

  1. Pin your Rust compiler version to the current stable, don't update until the ABI has changed. This means that you won't get any compiler warnings (as old compilers don't warn) and additionally you won't get broken when the ABI changes (as you're not changing compilers). Eventually when you update to a stable compiler with -Zwasm-c-abi=spec as the default you'll have to port your JS or bindings to work with the new ABI.

  2. Update to Rust nightly as your compiler and pass -Zwasm-c-abi=spec. This is front-loading the work required in (1) for your target. You can get your project compatible with -Zwasm-c-abi=spec today. The downside of this approach is that your project will only work with a nightly compiler and -Zwasm-c-abi=spec and you won't be able to use stable until the default is switched.

  3. Update your project to not rely on the non-standard behavior of -Zwasm-c-abi=legacy. This involves, for example, not passing structs-by-value in parameters. You can pass &Pair above, for example, instead of Pair. This is similar to (2) above where the work is done immediately to update a project but has the benefit of continuing to work on stable Rust. The downside of this, however, is that you may not be able to easily change or update your C ABI in some situations.

  4. Update to Rust nightly as your compiler and pass -Zwasm-c-abi=legacy. This will silence compiler warnings for now but be aware that the ABI will still change in the future and the -Zwasm-c-abi=legacy option will be removed entirely. When the -Zwasm-c-abi=legacy option is removed the only option will be the standard C ABI, what -Zwasm-c-abi=spec today enables.

If you have uncertainties, questions, or difficulties, feel free to reach out on the tracking issue for the future-incompat warning or on Zulip.

Timeline of ABI changes

At this time there is not an exact timeline of how the default ABI is going to change. It's expected to take on the order of 3-6 months, however, and will look something roughly like this:

  • 2025 March: (soon) - a future-incompat warning will be added to the compiler to warn projects if they're affected by this ABI change.
  • 2025-05-15: this future-incompat warning will reach the stable Rust channel as 1.87.0.
  • 2025 Summer: (ish) - the -Zwasm-c-abi flag will be removed from the compiler and the legacy option will be entirely removed.

Exactly when -Zwasm-c-abi is removed will depend on feedback from the community and whether the future-incompat warning triggers much. It's hoped that soon after the Rust 1.87.0 is stable, though, that the old legacy ABI behavior can be removed.