Skip to content

Support -Cpanic=unwind on WASI targets#156061

Open
alexcrichton wants to merge 1 commit intorust-lang:mainfrom
alexcrichton:unwinding-onw-asi
Open

Support -Cpanic=unwind on WASI targets#156061
alexcrichton wants to merge 1 commit intorust-lang:mainfrom
alexcrichton:unwinding-onw-asi

Conversation

@alexcrichton
Copy link
Copy Markdown
Member

@alexcrichton alexcrichton commented May 1, 2026

This commit is some minor updates/restructuring in a few locations with the end result being supporting -Cpanic=unwind on WASI targets. This continues to be off-by-default insofar as WASI targets default to -Cpanic=abort, meaning that actually using anything in this commit requires -Zbuild-std. Specifically the changes made here are:

  • The self-contained sysroot for WASI targets now contains a copy of libunwind.a from wasi-sdk, first shipped with wasi-sdk-33 (also updated here).
  • The unwind crate here in this repository uses the libunwind module instead of the custom bare-metal wasm implementation of exceptions. This means that Rust uses the _Unwind_* symbols which allows it to interoperate with C/C++/etc.
  • Wasm targets are all updated to pass the LLVM argument -wasm-use-legacy-eh=false to differ from LLVM's/clang's default of using the legacy exception handling proposal for WebAssembly. This has no effect by default because panic=abort is used on most targets. Emscripten is exempted from this as the Emscripten target is explicitly intended to follow LLVM's/clang's defaults.
  • There's a single test in the test suite that links to the panic_unwind crate which ended up requiring -Wexceptions from Wasmtime, so the test parts were updated and Wasmtime was updated in CI, too.

The net result of all of this is that this should not actually affect any WebAssembly target's default behavior. Optionally, though, WASI programs can be built with exception handling via:

RUSTFLAGS='-Cpanic=unwind' cargo +nightly run -Z build-std --target wasm32-wasip2

Effectively -Zbuild-std and -Cpanic=unwind is all that's necessary to enable this support on wasm targets.

Finally, this ends up closing #154593 as well. The WASI targets are now defined to use -lunwind to implement unwinding. This means that the in-tree definition of __cpp_exception is no longer of concern and the definition is always sourced externally. If Rust is linked with other C/C++ code using WASI then these idioms are compatible with wasi-sdk, for example, to use that as a linker. The main caveat is that when using an external linker the -fwasm-exceptions argument needs to be passed to clang for it to be able to find the libunwind.a library to link against.

Closes #154593

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 1, 2026

These commits modify compiler targets.
(See the Target Tier Policy.)

@rustbot rustbot added A-CI Area: Our Github Actions CI A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-infra Relevant to the infrastructure team, which will review and decide on the PR/issue. labels May 1, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 1, 2026

r? @marcoieni

rustbot has assigned @marcoieni.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: infra-ci
  • infra-ci expanded to Kobzol, Mark-Simulacrum, jdno, jieyouxu, marcoieni
  • Random selection from Mark-Simulacrum, jdno, jieyouxu, marcoieni

@rustbot

This comment has been minimized.

// actually turned on, which it's not by default on this target. For
// `-Zbuild-std` builds, however, this affects when rebuilding libstd
// with unwinding.
llvm_args: cvs!["-wasm-use-legacy-eh=false"],
Copy link
Copy Markdown
Member

@bjorn3 bjorn3 May 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't

// Returns `true` if this session's target will use native wasm
// exceptions. This means that the VM does the unwinding for
// us
pub fn wants_wasm_eh(sess: &Session) -> bool {
sess.target.is_like_wasm
&& (sess.target.os != Os::Emscripten || sess.opts.unstable_opts.emscripten_wasm_eh)
}
already supposed to do this?

Edit: Might be confused with wasm vs js exception handling.

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe, yeah, that's related to the Emscripten-specific scheme of exceptions pre-wasm-exceptions of using JS instead. Effectively there's 3 modes of exceptions on Wasm:

  1. Historical JS-based support for Emscripten. That's used when wants_wasm_eh is false and is only applicable to historical versions of the Emscripten target (or -Zemscripten-wasm-eh=false)
  2. Historical wasm support. This uses the now-legacy exception-handling proposal that shipped in browsers but was never standardized. This uses wasm instructions and has wants_wasm_eh is true. LLVM by default emits this today.
  3. Modern wasm support. This uses the now-standard exception-handling proposal as the successor to the "legacy exceptions" of above. This is wants_wasm_eh is true plus an option to LLVM to use the new instructions.

For (1) the LLVM IR is different, and for (2) and (3) they're the same IR. I don't know much about (1) myself. For WASI this PR only exposes (3)

@marcoieni
Copy link
Copy Markdown
Member

The changes in src/ci/docker look good. Someone else should review the other files since I'm not familiar with them.

@alexcrichton
Copy link
Copy Markdown
Member Author

@bjorn3 would you be up for reviewing the non-infra-related bits?

Comment thread library/unwind/src/libunwind.rs
Comment thread src/bootstrap/src/core/build_steps/compile.rs
Comment thread src/bootstrap/src/lib.rs
&& let Ok(mut path) = path.into_os_string().into_string()
{
path.push_str(" run -C cache=n --dir .");
path.push_str(" run -Wexceptions -C cache=n --dir .");
Copy link
Copy Markdown
Member

@bjorn3 bjorn3 May 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no tests added that use exceptions, right?

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost yeah, but there's one test that does extern crate panic_unwind; which pulls in libunwind which pulls in a throw instruction which then needs this to validate

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised that even works without //@ needs-unwind to only run the test for panic=unwind. I thought rustc refused to link a panic runtime that doesn't match the -Cpanic=..., but I guess that check is only done when the panic runtime dependency is injected by rustc, not when it is explicitly done by the user.

This commit is some minor updates/restructuring in a few locations with
the end result being supporting `-Cpanic=unwind` on WASI targets. This
continues to be off-by-default insofar as WASI targets default to
`-Cpanic=abort`, meaning that actually using anything in this commit
requires `-Zbuild-std`. Specifically the changes made here are:

* The self-contained sysroot for WASI targets now contains a copy of
  `libunwind.a` from wasi-sdk, first shipped with wasi-sdk-33 (also
  updated here).
* The `unwind` crate here in this repository uses the `libunwind` module
  instead of the custom bare-metal wasm implementation of exceptions.
  This means that Rust uses the `_Unwind_*` symbols which allows it to
  interoperate with C/C++/etc.
* Wasm targets are all updated to pass the LLVM argument
  `-wasm-use-legacy-eh=false` to differ from LLVM's/clang's default of
  using the legacy exception handling proposal for WebAssembly. This has
  no effect by default because `panic=abort` is used on most targets.
  Emscripten is exempted from this as the Emscripten target is
  explicitly intended to follow LLVM's/clang's defaults.
* There's a single test in the test suite that links to the
  `panic_unwind` crate which ended up requiring `-Wexceptions` from
  Wasmtime, so the test parts were updated and Wasmtime was updated in
  CI, too.

The net result of all of this is that this should not actually affect
any WebAssembly target's default behavior. Optionally, though, WASI
programs can be built with exception handling via:

    RUSTFLAGS='-Cpanic=unwind' cargo +nightly run -Z build-std --target wasm32-wasip2

Effectively `-Zbuild-std` and `-Cpanic=unwind` is all that's necessary
to enable this support on wasm targets.

Finally, this ends up closing 154593 as well. The WASI targets are now
defined to use `-lunwind` to implement unwinding. This means that the
in-tree definition of `__cpp_exception` is no longer of concern and the
definition is always sourced externally. If Rust is linked with other
C/C++ code using WASI then these idioms are compatible with wasi-sdk,
for example, to use that as a linker. The main caveat is that when using
an external linker the `-fwasm-exceptions` argument needs to be passed
to `clang` for it to be able to find the `libunwind.a` library to link
against.

Closes 154593
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented May 5, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@bjorn3
Copy link
Copy Markdown
Member

bjorn3 commented May 6, 2026

@bors r+

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors Bot commented May 6, 2026

📌 Commit 13c6e60 has been approved by bjorn3

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CI Area: Our Github Actions CI A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-infra Relevant to the infrastructure team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WASI: -Cpanic=unwind does not work

4 participants