feat(wasm): link libsetjmp, so setjmp needs no flag
All checks were successful
Flatpak / flatpak (push) Successful in 17m58s
All checks were successful
Flatpak / flatpak (push) Successful in 17m58s
-mllvm -wasm-enable-sjlj, in every wasm compile because setjmp.h does not compile without it, turns setjmp and longjmp into calls to __wasm_setjmp, __wasm_setjmp_test and __wasm_longjmp and a throw of the __c_longjmp tag, all of which live in wasi-libc's libsetjmp. The driver does not link it, so a program that used setjmp, or a C library that handles its errors that way, failed to link with undefined __wasm_setjmp until the project put -lsetjmp in its linkFlags. Build() adds it to every wasm link, next to -lunwind. A static archive contributes only the members something references, so a program without setjmp gets nothing from it, and every sysroot that passes the exception check ships it. WasmExceptions drops its own -lsetjmp, and its C fixture now fails to link without this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
3e125d83f0
commit
70ff11d0a1
3 changed files with 10 additions and 7 deletions
|
|
@ -189,7 +189,7 @@ For `--target=x86_64-w64-mingw32` (Windows from Linux), the pipeline cross-compi
|
|||
|
||||
For `--target=wasm32-wasip1`, the WASI sysroot is the one beside the toolchain (the Flatpak bundles one) unless `CRAFTER_BUILD_WASI_SYSROOT` names another, and tests run under `wasmtime`. C++ exceptions work as on the other targets: every unit and the std module are compiled with `-fwasm-exceptions` in the standardized form of WebAssembly exception handling (exnref), against the exception-enabled libc++ in the sysroot's `lib/<triple>/eh/`, and `-lunwind` is linked automatically. A sysroot without that libc++ (wasi-sdk before 33) stops the build with one line saying so. The result runs on engines with that proposal: Chrome 137, Firefox 131 and Safari 18.4 or newer, and the wasmtime the Flatpak bundles. A page from `EnableWasiBrowserRuntime` checks first and names what an older browser lacks in the console, instead of failing to compile the module. An exception that escapes `main` leaves the program as an uncaught WebAssembly exception (wasmtime reports `thrown Wasm exception` and exits 1, a page gets a `WebAssembly.Exception`) without calling `std::terminate`, so a `std::set_terminate` handler does not run and nothing prints the exception's type or `what()`: wasm exception handling unwinds in a single phase, and nothing finds out that no handler is left until the stack is gone. Catch it in `main` to report it.
|
||||
|
||||
A wasm build refuses three things before it starts, each with a message that says why: `-flto` in `compileFlags` or `linkFlags` (LTO code generation drops the exception handling, [WebAssembly/wasi-sdk#629](https://github.com/WebAssembly/wasi-sdk/issues/629)), `-fno-exceptions` (the std module is built with exceptions, and a unit without them cannot import it) and a shared library (the exception-enabled libc++ is static archives only). `setjmp` and `longjmp` work in C and C++ sources once `-lsetjmp` is in `linkFlags`; a `longjmp` across a `noexcept` function ends the program, because it is a wasm exception and the function's terminate handler catches it. Outside the Flatpak, a wasmtime older than 47 has exception handling off and needs `-W exceptions=y`, and a `wasm-opt` on `PATH` from a binaryen that cannot read the standardized encoding breaks release links, since clang runs it on optimized wasm links when it finds one.
|
||||
A wasm build refuses three things before it starts, each with a message that says why: `-flto` in `compileFlags` or `linkFlags` (LTO code generation drops the exception handling, [WebAssembly/wasi-sdk#629](https://github.com/WebAssembly/wasi-sdk/issues/629)), `-fno-exceptions` (the std module is built with exceptions, and a unit without them cannot import it) and a shared library (the exception-enabled libc++ is static archives only). `setjmp` and `longjmp` work in C and C++ sources, with `-lsetjmp` linked automatically like `-lunwind`; a `longjmp` across a `noexcept` function ends the program, because it is a wasm exception and the function's terminate handler catches it. Outside the Flatpak, a wasmtime older than 47 has exception handling off and needs `-W exceptions=y`, and a `wasm-opt` on `PATH` from a binaryen that cannot read the standardized encoding breaks release links, since clang runs it on optimized wasm links when it finds one.
|
||||
|
||||
In a page from `EnableWasiBrowserRuntime`, `exit` and `_Exit` end the program as they would anywhere else: none of its code runs on the way out, no destructor and no `catch (...)`, its status is in `window.crafter_wasi.exitCode`, and bridges that call into it on their own are expected to stop. A program that hands the thread to the browser for good and goes on serving calls, as Crafter.Graphics's `Window::StartSync()` does, parks instead of exiting. Parking never returns, runs nothing of the program either, and leaves `main`'s locals valid for the calls that follow:
|
||||
|
||||
|
|
|
|||
|
|
@ -1208,9 +1208,14 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
}
|
||||
// Code built with -fwasm-exceptions calls into libunwind (the
|
||||
// __cpp_exception tag, the landing-pad context, _Unwind_RaiseException),
|
||||
// and the driver does not add it to the link the way it adds libc++.
|
||||
// and -wasm-enable-sjlj turns setjmp and longjmp into calls into
|
||||
// wasi-libc's libsetjmp (__wasm_setjmp, __wasm_longjmp, the __c_longjmp
|
||||
// tag). The driver adds neither to the link the way it adds libc++. A
|
||||
// static archive contributes only what something references, so a
|
||||
// program without setjmp gets nothing from libsetjmp, and every sysroot
|
||||
// that passes WasmBuildError ships both.
|
||||
if (isWasm) {
|
||||
linkExtras += " -lunwind";
|
||||
linkExtras += " -lunwind -lsetjmp";
|
||||
}
|
||||
|
||||
// Force a relink if the expected output is missing or older than any dep
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ using namespace Crafter;
|
|||
// libc++.a itself (stoi), and with an argument lets one escape main, which
|
||||
// has to end the run as an uncaught exception rather than exit 0, trap or
|
||||
// hang. Its C source uses setjmp, which compiles and links next to the C++
|
||||
// objects only when C gets the same wasm flags.
|
||||
// objects only when C gets the same wasm flags, and links at all only
|
||||
// because the build adds libsetjmp: the configuration asks for nothing.
|
||||
//
|
||||
// wasm builds used to be compiled with -fno-exceptions, so a throw did not
|
||||
// compile and libc++'s own throws aborted. Each part of turning them on fails
|
||||
|
|
@ -55,9 +56,6 @@ int main() {
|
|||
std::array<fs::path, 1> impls = { "main" };
|
||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||
cfg.cFiles = { cfg.path / "jump" };
|
||||
// setjmp and longjmp live in libsetjmp, which the build does not link
|
||||
// on its own.
|
||||
cfg.linkFlags.push_back("-lsetjmp");
|
||||
|
||||
std::unordered_map<fs::path, std::shared_future<BuildResult>> depResults;
|
||||
std::mutex depMutex;
|
||||
|
|
|
|||
Loading…
Reference in a new issue