From 3f0e04a053bc79cc4c71e0e872a3079fd340576f Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 3 Mar 2026 23:40:51 +0100 Subject: [PATCH 01/26] Build `wasip3` target (#593) Build `wasip3` target ~Requires #592~ There's a lot of failing tests currently, so we'll probably want to rebase this PR as we pull in more recent versions of `wasi-libc` and merge once tests pass --------- Signed-off-by: Roman Volosatovs Co-authored-by: Alex Crichton --- cmake/wasi-sdk-sysroot.cmake | 20 +++++++++++++----- cmake/wasi-sdk-toolchain.cmake | 1 + tests/CMakeLists.txt | 3 +++ wasi-sdk-p3.cmake | 37 ++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 wasi-sdk-p3.cmake diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index c19d0c6..38f804c 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -101,12 +101,23 @@ function(define_compiler_rt target) add_dependencies(compiler-rt-build compiler-rt-build-${target}) endfunction() +# The `compiler-rt` for `wasm32-wasip1` will be reused for `wasm32-wasip2` and +# `wasm32-wasi`. The version for `wasm32-wasip1-threads` will be reused for +# `wasm32-wasi-threads`. Different builds are needed for different codegen flags +# and such across the threaded/not target. define_compiler_rt(wasm32-wasip1) define_compiler_rt(wasm32-wasip1-threads) +# If a p3 target is requested, also build compiler-rt for that target. WASIp3 +# will eventually have a different ABI than wasm32-wasip2, so this separate +# build is needed. +if(WASI_SDK_TARGETS MATCHES p3) + define_compiler_rt(wasm32-wasip3) +endif() + # In addition to the default installation of `compiler-rt` itself also copy # around some headers and make copies of the `wasi` directory as `wasip1` and -# `wasip2` +# `wasip2` and `wasip3` execute_process( COMMAND ${CMAKE_C_COMPILER} -print-resource-dir OUTPUT_VARIABLE clang_resource_dir @@ -134,7 +145,6 @@ add_dependencies(compiler-rt-post-build compiler-rt-build) add_custom_target(compiler-rt DEPENDS compiler-rt-build compiler-rt-post-build) - # ============================================================================= # wasi-libc build logic # ============================================================================= @@ -144,9 +154,9 @@ function(define_wasi_libc_sub target target_suffix lto) get_property(directory_cflags DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY COMPILE_OPTIONS) set(extra_cflags_list "${WASI_SDK_CPU_CFLAGS} ${CMAKE_C_FLAGS} ${directory_cflags}") - if(${target} MATCHES p2) - # Always enable `-fPIC` for the `wasm32-wasip2` target. This makes `libc.a` - # more flexible and usable in dynamic linking situations. + if(${target} MATCHES "p[23]") + # Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets. + # This makes `libc.a` more flexible and usable in dynamic linking situations. list(APPEND extra_cflags_list -fPIC) endif() diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index c02560d..848bd26 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -156,6 +156,7 @@ copy_misc_file(wasi-sdk.cmake cmake) copy_misc_file(wasi-sdk-pthread.cmake cmake) copy_misc_file(wasi-sdk-p1.cmake cmake) copy_misc_file(wasi-sdk-p2.cmake cmake) +copy_misc_file(wasi-sdk-p3.cmake cmake) copy_misc_file(cmake/Platform/WASI.cmake cmake/Platform) function(copy_cfg_file compiler) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ed4776..c573aea 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -102,6 +102,9 @@ function(add_testcase runwasi test) if(WASI_SDK_EXCEPTIONS) set(runner "${runner} -Wexceptions") endif() + if(target MATCHES "wasip3") + set(runner "${runner} -Wcomponent-model-async -Sp3") + endif() endif() add_test( NAME test-${target_name} diff --git a/wasi-sdk-p3.cmake b/wasi-sdk-p3.cmake new file mode 100644 index 0000000..f737562 --- /dev/null +++ b/wasi-sdk-p3.cmake @@ -0,0 +1,37 @@ +# Cmake toolchain description file for the Makefile + +# Until Platform/WASI.cmake is upstream we need to inject the path to it +# into CMAKE_MODULE_PATH. +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + +set(CMAKE_SYSTEM_NAME WASI) +set(CMAKE_SYSTEM_VERSION 1) +set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(triple wasm32-wasip3) + +if(WIN32) + set(WASI_HOST_EXE_SUFFIX ".exe") +else() + set(WASI_HOST_EXE_SUFFIX "") +endif() + +# When building from source, WASI_SDK_PREFIX represents the generated directory +if(NOT WASI_SDK_PREFIX) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) +endif() + +set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_CXX_COMPILER ${WASI_SDK_PREFIX}/bin/clang++${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_ASM_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_AR ${WASI_SDK_PREFIX}/bin/llvm-ar${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_RANLIB ${WASI_SDK_PREFIX}/bin/llvm-ranlib${WASI_HOST_EXE_SUFFIX}) +set(CMAKE_C_COMPILER_TARGET ${triple}) +set(CMAKE_CXX_COMPILER_TARGET ${triple}) +set(CMAKE_ASM_COMPILER_TARGET ${triple}) + +# Don't look in the sysroot for executables to run during the build +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# Only look in the sysroot (not in the host paths) for the rest +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) From 9713459bcf52ca2b9bf8cecdef1eff73c332ba6b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Mar 2026 17:24:00 -0600 Subject: [PATCH 02/26] Include `libclang.so` in wasi-sdk (#598) This includes a new library, `libclang.so`, which is Clang's public C API. This is used by tooling such as Rust's `bindgen` library to generate bindings and can be useful when bindings are generated using a wasi-sdk distribution. --- cmake/wasi-sdk-toolchain.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 848bd26..1cc373d 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -57,7 +57,8 @@ set(tools objdump objcopy c++filt - llvm-config) + llvm-config + libclang) # By default link LLVM dynamically to all the various tools. This greatly # reduces the binary size of all the tools through a shared library rather than From eacaeb6be38eb91806983e8494bf3854766f899b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 3 Mar 2026 18:58:44 -0600 Subject: [PATCH 03/26] Update wasi-libc submodule (#599) Notably pull in some fixes for the wasip2 target related to networking. --- src/wasi-libc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi-libc b/src/wasi-libc index ec0effd..58fa18a 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit ec0effd769df5f05b647216578bcf5d3b5449725 +Subproject commit 58fa18a968de915bea7d962485d9838693779bf9 From 4ec071867cec6aaee9a1010885b9ea06d322c732 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Mar 2026 19:04:20 -0600 Subject: [PATCH 04/26] Build LLDB as part of wasi-sdk (#596) This is intended to provide a known-good build of LLDB with wasm plugin support which can exist alongside the rest of the LLVM distribution provided by wasi-sdk. --- ci/docker/Dockerfile | 3 +- cmake/wasi-sdk-toolchain.cmake | 76 ++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/ci/docker/Dockerfile b/ci/docker/Dockerfile index a5da2c8..71f4a1d 100644 --- a/ci/docker/Dockerfile +++ b/ci/docker/Dockerfile @@ -12,7 +12,8 @@ RUN dnf install -y \ python3 \ git \ unzip \ - cmake + cmake \ + ncurses-devel COPY ./install-ccache.sh . RUN ./install-ccache.sh diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 1cc373d..04c0a5d 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -5,6 +5,21 @@ set(LLVM_CMAKE_FLAGS "" CACHE STRING "Extra cmake flags to pass to LLVM's build" set(RUST_TARGET "" CACHE STRING "Target to build Rust code for, if not the host") set(WASI_SDK_ARTIFACT "" CACHE STRING "Name of the wasi-sdk artifact being produced") +option(WASI_SDK_LLDB "Include a build of LLDB" ON) + +set(LIBEDIT_DEFAULT ON) +# I don't want to deal with running a `./configure` script on Windows, disable +# it by default. +if(WIN32) + set(LIBEDIT_DEFAULT OFF) +endif() +# I don't know how to resolve build failures when building libedit for x86_64 +# from arm64 on macos, so disable it for now. +if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND LLVM_CMAKE_FLAGS MATCHES "x86_64") + set(LIBEDIT_DEFAULT OFF) +endif() +option(WASI_SDK_LIBEDIT "Whether or not to build libedit for LLDB" ${LIBEDIT_DEFAULT}) + string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}") set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) @@ -73,6 +88,66 @@ if(NOT WIN32) list(APPEND tools LLVM clang-cpp) endif() +# Configure/add LLDB if requested. +# +# Note that LLDB depends on `libedit` which is more-or-less required to get a +# reasonable command-line experience, so this is built custom here to ensure +# that it's available for LLDB. +if(WASI_SDK_LLDB) + list(APPEND projects lldb) + list(APPEND tools lldb liblldb) + list(APPEND default_cmake_args + -DLLDB_INCLUDE_TESTS=OFF + -DLLDB_INCLUDE_UNITTESTS=OFF + -DLLDB_ENABLE_SWIG=OFF + -DLLDB_ENABLE_CURSES=OFF + -DLLDB_ENABLE_LZMA=OFF + -DLLDB_ENABLE_LUA=OFF + -DLLDB_ENABLE_PYTHON=OFF + -DLLDB_ENABLE_LIBXML2=OFF + -DLLDB_ENABLE_FBSDVMCORE=OFF + -DLLDB_ENABLE_LINUXPTY=OFF + ) + + if (WASI_SDK_LIBEDIT) + include(ProcessorCount) + ProcessorCount(nproc) + find_program(MAKE_EXECUTABLE make REQUIRED) + ExternalProject_Add(libedit + URL https://thrysoee.dk/editline/libedit-20251016-3.1.tar.gz + URL_HASH SHA256=21362b00653bbfc1c71f71a7578da66b5b5203559d43134d2dd7719e313ce041 + + # Without this the build system tries to find and use `aclocal-1.18` where + # with this it doesn't so turn this on. + DOWNLOAD_EXTRACT_TIMESTAMP ON + + CONFIGURE_COMMAND + /configure + --prefix=${wasi_tmp_install} + --enable-pic + --disable-examples + CC=${CMAKE_C_COMPILER} + CFLAGS=${libedit_cflags} + LDFLAGS=${libedit_cflags} + BUILD_COMMAND + ${MAKE_EXECUTABLE} -j${nproc} V=1 + + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD ON + USES_TERMINAL_INSTALL ON + ) + list(APPEND default_cmake_args + -DLLDB_ENABLE_LIBEDIT=ON + -DLibEdit_ROOT=${wasi_tmp_install} + ) + else() + list(APPEND default_cmake_args -DLLDB_ENABLE_LIBEDIT=OFF) + add_custom_target(libedit) + endif() +else() + add_custom_target(libedit) +endif() + list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets) list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets) @@ -110,6 +185,7 @@ ExternalProject_Add(llvm-build ) add_custom_target(build ALL DEPENDS llvm-build) +ExternalProject_Add_StepDependencies(llvm-build configure libedit) # Installation target for this outer project for installing the toolchain to the # system. From ab2be6e0dfa96160921e6b2f68e67a3e26ba32cb Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Mar 2026 09:43:29 -0600 Subject: [PATCH 05/26] Adjust documentation of limitations in README (#591) * Update for C++ Exceptions being supported. * Update the threading-related bits slightly. * Indicate that dynamic linking works but may not be as fully tested. * Networking works, just not on WASIp1. --- README.md | 59 ++++++++++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 97bb4f5..7d28f69 100644 --- a/README.md +++ b/README.md @@ -209,41 +209,30 @@ disabled in a configure step before building with WASI SDK. ## Notable Limitations -This repository does not yet support __C++ exceptions__. C++ code is supported -only with -fno-exceptions for now. -Work on support for [exception handling] is underway at the -language level which will support the features. - -[exception handling]: https://github.com/WebAssembly/exception-handling/ - -See [C setjmp/longjmp support] about setjmp/longjmp support. - -[C setjmp/longjmp support]: SetjmpLongjmp.md - -This repository experimentally supports __threads__ with -`--target=wasm32-wasip1-threads`. It uses WebAssembly's [threads] primitives -(atomics, `wait`/`notify`, shared memory) and [wasi-threads] for spawning -threads. Note: this is experimental — do not expect long-term stability! - -Note that the `pthread_*` family of functions, as well as C++ threading primitives -such as ``, ``, and `` are available on all targets. -Any attempt to spawn a thread will fail on `--target=wasm32-wasip1` or -`--target=wasm32-wasip2`, but other functionality, such as locks, still works. -This makes it easier to port C++ codebases, as only a fraction of code needs -to be modified to build for the single-threaded targets. - -Defining a macro `_WASI_STRICT_PTHREAD` will make `pthread_create`, -`pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and `pthread_timedjoin_np` -fail with a compile time error when building for single-threaded targets. +* C++ exceptions are disabled by default. For more information see + [CppExceptions.md]. +* C `setjmp`/`longjmp` require some extra configuration to get working, see + [SetjmpLongjmp.md]. +* Most targets do not support spawning a thread. Experimental support for + spawning threads is available with the `wasm32-wasip1-threads` target which + uses [wasi-threads]. Note that the `pthread_*` family of functions, as well as + C++ threading primitives such as ``, ``, and `` are + available on all targets. Defining a macro `_WASI_STRICT_PTHREAD` will make + `pthread_create`, `pthread_detach`, `pthread_join`, `pthread_tryjoin_np`, and + `pthread_timedjoin_np` fail with a compile time error when building for + single-threaded targets. +* Dynamic linking [is supported][dylink] but not as fully baked as static + linking. There might be obscure bugs in some situations related to dynamic + linking. +* The WASIp1 targets do not support networking, but WASIp2/WASIp3 support + networking. +* 64-bit linear memories (a "wasm64" target) are not supported at this time. + Supporting this will require resolving [WebAssembly/component-model#22] first + at which point it will be possible to add a `wasm64-wasip2` target. There are + no plans to add support for `wasm64-wasi{,-threads,p1,p1-threads}` at this + time. [threads]: https://github.com/WebAssembly/threads [wasi-threads]: https://github.com/WebAssembly/wasi-threads - -This repository does not yet support __dynamic libraries__. While there are -[some efforts] to design a system for dynamic libraries in wasm, it is still in -development and not yet generally usable. - -[some efforts]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md - -There is no support for __networking__. It is a goal of WASI to support -networking in the future though. +[dylink]: https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md +[WebAssembly/component-model#22]: https://github.com/WebAssembly/component-model/issues/22 From 4d474b1b7d22c28d1bb2217ad6fb89b2fea84a47 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 5 Mar 2026 11:02:26 -0600 Subject: [PATCH 06/26] Change Clang's default target to `wasm32-wasip1` (#600) No longer use `wasm32-wasi` since Clang issues a deprecation warning about that. This fixes the `clang` binary, by default, emitting a warning for example in the final build. --- cmake/wasi-sdk-toolchain.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 04c0a5d..21cdb39 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -163,7 +163,7 @@ ExternalProject_Add(llvm-build -DLLVM_INCLUDE_BENCHMARKS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_TARGETS_TO_BUILD=WebAssembly - -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasi + -DLLVM_DEFAULT_TARGET_TRIPLE=wasm32-wasip1 -DLLVM_INSTALL_BINUTILS_SYMLINKS=TRUE -DLLVM_ENABLE_LIBXML2=OFF # Pass `-s` to strip symbols by default and shrink the size of the From 77553563c8a2672d97fdb97c04923da3f89e8017 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 10 Mar 2026 18:03:42 -0500 Subject: [PATCH 07/26] Update wasi-libc (#602) Once more before wasi-sdk-31 --- src/wasi-libc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi-libc b/src/wasi-libc index 58fa18a..724d866 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit 58fa18a968de915bea7d962485d9838693779bf9 +Subproject commit 724d86644e1972fe99fcb598c5f2f9a179cfec10 From 2e6b578a6986005e29e01c10b94843fdb2a76f0a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 Mar 2026 12:40:53 -0500 Subject: [PATCH 08/26] Update wasi-libc one more time (#604) Fix an issue with setjmp/longjmp when combined with LLVM 22. --- src/wasi-libc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi-libc b/src/wasi-libc index 724d866..2fc32bc 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit 724d86644e1972fe99fcb598c5f2f9a179cfec10 +Subproject commit 2fc32bc81b9f07f8d9525edea59bfbaf760c06d6 From 015c849728814e668669b6e1f74c782d0f6b150e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 Mar 2026 17:18:33 -0500 Subject: [PATCH 09/26] Update github actions workflows (#605) Handle warnings cropping up in CI --- .github/workflows/main.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b39e65..14d51b5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,7 +53,7 @@ jobs: env: ${{ matrix.env || fromJSON('{}') }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: ./.github/actions/checkout @@ -65,7 +65,7 @@ jobs: # Bump the prefix number to evict all previous caches and enforce a clean # build, in the unlikely case that some weird build error occur and ccache # becomes a potential suspect. - - uses: actions/cache@v4 + - uses: actions/cache@v5 id: cache-restore with: path: ${{ runner.tool_cache }}/ccache @@ -125,7 +125,7 @@ jobs: # Upload the `dist` folder from the build as the artifacts for this # runner. - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: ${{ format( 'dist-{0}', matrix.artifact) }} path: build/dist @@ -149,7 +149,7 @@ jobs: # time instead of having to recreate everything each time a failure # happens. - if: always() && steps.cache-restore.outputs.cache-hit != 'true' - uses: actions/cache/save@v4 + uses: actions/cache/save@v5 with: path: ${{ runner.tool_cache }}/ccache key: 0-cache-${{ matrix.artifact }}-${{ github.run_id }} @@ -164,7 +164,7 @@ jobs: - name: exceptions defines: -DWASI_SDK_EXCEPTIONS=ON steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: ./.github/actions/checkout @@ -191,16 +191,16 @@ jobs: needs: build runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: ./.github/actions/checkout # Download all artifacts from all platforms in `build`, merge them into # final wasi-sdk-* artifacts, and then upload them. - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 - run: ./ci/merge-artifacts.sh - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v6 with: name: release-artifacts path: dist @@ -251,12 +251,12 @@ jobs: needs: build runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: ./.github/actions/checkout - uses: ./.github/actions/install-deps - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v8 with: name: dist-x86_64-linux path: dist-x86_64-linux From 5d5c6c0d1898912df6ed16e08eef5c667c46f151 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 11 Mar 2026 19:18:15 -0500 Subject: [PATCH 10/26] Attempt to fix finding libedit on macOS (#609) First pass at trying to fix this... --- cmake/wasi-sdk-toolchain.cmake | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 21cdb39..56920f7 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -113,6 +113,9 @@ if(WASI_SDK_LLDB) include(ProcessorCount) ProcessorCount(nproc) find_program(MAKE_EXECUTABLE make REQUIRED) + if(CMAKE_SYSTEM_NAME STREQUAL Darwin) + set(libedit_ldflags -Wl,-install_name,@rpath/libedit.0.dylib) + endif() ExternalProject_Add(libedit URL https://thrysoee.dk/editline/libedit-20251016-3.1.tar.gz URL_HASH SHA256=21362b00653bbfc1c71f71a7578da66b5b5203559d43134d2dd7719e313ce041 @@ -127,8 +130,7 @@ if(WASI_SDK_LLDB) --enable-pic --disable-examples CC=${CMAKE_C_COMPILER} - CFLAGS=${libedit_cflags} - LDFLAGS=${libedit_cflags} + LDFLAGS=${libedit_ldflags} BUILD_COMMAND ${MAKE_EXECUTABLE} -j${nproc} V=1 @@ -169,6 +171,9 @@ ExternalProject_Add(llvm-build # Pass `-s` to strip symbols by default and shrink the size of the # distribution -DCMAKE_EXE_LINKER_FLAGS=-s + # Looks to be required on macOS for, at build time, the dynamic linker to + # find `libedit.dylib` when that's enabled. + -DCMAKE_BUILD_RPATH=${wasi_tmp_install}/lib ${llvm_cmake_flags_list} # See https://www.scivision.dev/cmake-externalproject-list-arguments/ for # why this is in `CMAKE_CACHE_ARGS` instead of above From 0991cf438b7064f87cbe1deb2d5fb6b7f64df150 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 12 Mar 2026 14:28:14 -0500 Subject: [PATCH 11/26] Fix C++ thread locals, add a test (#611) Pulls in llvm/llvm-project#186054 and adds a regression test which previously failed. Closes #610 --- cmake/wasi-sdk-sysroot.cmake | 3 ++ src/llvm-pr-186054.patch | 48 +++++++++++++++++++++++++++++++ tests/general/CMakeLists.txt | 4 +-- tests/general/cpp_thread_local.cc | 3 ++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/llvm-pr-186054.patch create mode 100644 tests/general/cpp_thread_local.cc diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 38f804c..38ed426 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -330,6 +330,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ PATCH_COMMAND ${CMAKE_COMMAND} -E chdir .. bash -c "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check" + COMMAND + ${CMAKE_COMMAND} -E chdir .. bash -c + "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch -R --check" ) endfunction() diff --git a/src/llvm-pr-186054.patch b/src/llvm-pr-186054.patch new file mode 100644 index 0000000..c7756d7 --- /dev/null +++ b/src/llvm-pr-186054.patch @@ -0,0 +1,48 @@ +From f71fdfcbd6fcc7b521c74b5856ebeacdd6cf55d9 Mon Sep 17 00:00:00 2001 +From: Catherine +Date: Thu, 12 Mar 2026 08:19:49 +0000 +Subject: [PATCH] [libc++abi] Revert gating of `__cxa_thread_atexit` on + Linux||Fuchsia + +This was done in the commit 3c100d5d548d with the description +"Enable -Wmissing-prototypes" which seems incongruent to me. + +Since then it's made its way into a release and broke the use of +`thread_local` variables with destructors on Wasm/WASI: + +```cc +// repro.cc +struct c { ~c() {} }; +thread_local c v; +int main() { (void)v; } +``` + +```console +$ ./wasi-sdk-31.0-x86_64-linux/bin/clang++ repro.cc +wasm-ld: error: /tmp/repro-dd1ad7.o: undefined symbol: __cxa_thread_atexit +clang++: error: linker command failed with exit code 1 (use -v to see invocation) +``` +--- + libcxxabi/src/cxa_thread_atexit.cpp | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/libcxxabi/src/cxa_thread_atexit.cpp b/libcxxabi/src/cxa_thread_atexit.cpp +index 402a52c741012..1bdcb4ef192b4 100644 +--- a/libcxxabi/src/cxa_thread_atexit.cpp ++++ b/libcxxabi/src/cxa_thread_atexit.cpp +@@ -106,7 +106,6 @@ namespace { + + #endif // HAVE___CXA_THREAD_ATEXIT_IMPL + +-#if defined(__linux__) || defined(__Fuchsia__) + extern "C" { + + _LIBCXXABI_FUNC_VIS int __cxa_thread_atexit(Dtor dtor, void* obj, void* dso_symbol) throw() { +@@ -141,6 +140,5 @@ extern "C" { + } + #endif // HAVE___CXA_THREAD_ATEXIT_IMPL + } +-} // extern "C" +-#endif // defined(__linux__) || defined(__Fuchsia__) ++ } // extern "C" + } // namespace __cxxabiv1 diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt index ef0a552..2954fd1 100644 --- a/tests/general/CMakeLists.txt +++ b/tests/general/CMakeLists.txt @@ -1,5 +1,5 @@ -file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") -file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") +file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.c") +file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.cc") set(general_tests ${c_general_tests} ${cxx_general_tests}) diff --git a/tests/general/cpp_thread_local.cc b/tests/general/cpp_thread_local.cc new file mode 100644 index 0000000..30a1518 --- /dev/null +++ b/tests/general/cpp_thread_local.cc @@ -0,0 +1,3 @@ +struct c { ~c() {} }; +thread_local c v; +int main() { (void)v; } From d5030e3d63839ca618442b5e97d1d5cff3be0ab0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 13 Mar 2026 17:42:33 -0500 Subject: [PATCH 12/26] Build libxml2 for LLDB (#614) Attempting to fix #612 --- cmake/wasi-sdk-toolchain.cmake | 39 ++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 56920f7..ef977c3 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -19,6 +19,7 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" AND LLVM_CMAKE_FLAGS MATCHES "x86_64") set(LIBEDIT_DEFAULT OFF) endif() option(WASI_SDK_LIBEDIT "Whether or not to build libedit for LLDB" ${LIBEDIT_DEFAULT}) +option(WASI_SDK_LIBXML2 "Whether or not to build libxml2 for LLDB" ON) string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}") @@ -104,7 +105,6 @@ if(WASI_SDK_LLDB) -DLLDB_ENABLE_LZMA=OFF -DLLDB_ENABLE_LUA=OFF -DLLDB_ENABLE_PYTHON=OFF - -DLLDB_ENABLE_LIBXML2=OFF -DLLDB_ENABLE_FBSDVMCORE=OFF -DLLDB_ENABLE_LINUXPTY=OFF ) @@ -146,8 +146,43 @@ if(WASI_SDK_LLDB) list(APPEND default_cmake_args -DLLDB_ENABLE_LIBEDIT=OFF) add_custom_target(libedit) endif() + + set(libxml_cmake_args) + + # Windows doesn't have iconv by default, so disable it for now. + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + list(APPEND libxml_cmake_args -DLIBXML2_WITH_ICONV=OFF) + endif() + + if (WASI_SDK_LIBXML2) + ExternalProject_Add(libxml2 + URL https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.2.tar.xz + URL_HASH SHA256=c8b9bc81f8b590c33af8cc6c336dbff2f53409973588a351c95f1c621b13d09d + + CMAKE_ARGS + ${default_cmake_args} + -DLIBXML2_WITH_PROGRAMS=OFF + -DLIBXML2_WITH_DEBUG=OFF + -DLIBXML2_WITH_DOCS=OFF + -DLIBXML2_WITH_TESTS=OFF + ${libxml_cmake_args} + ${llvm_cmake_flags_list} + + USES_TERMINAL_CONFIGURE ON + USES_TERMINAL_BUILD ON + USES_TERMINAL_INSTALL ON + ) + list(APPEND default_cmake_args + -DLLDB_ENABLE_LIBXML2=ON + -DLibXml2_ROOT=${wasi_tmp_install} + ) + else() + list(APPEND default_cmake_args -DLLDB_ENABLE_LIBXML2=OFF) + add_custom_target(libxml2) + endif() else() add_custom_target(libedit) + add_custom_target(libxml2) endif() list(TRANSFORM tools PREPEND --target= OUTPUT_VARIABLE build_targets) @@ -190,7 +225,7 @@ ExternalProject_Add(llvm-build ) add_custom_target(build ALL DEPENDS llvm-build) -ExternalProject_Add_StepDependencies(llvm-build configure libedit) +ExternalProject_Add_StepDependencies(llvm-build configure libedit libxml2) # Installation target for this outer project for installing the toolchain to the # system. From cc5c4c98d7e5945a74759a1f40c26239bd7a4b1a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 16 Mar 2026 11:16:00 -0500 Subject: [PATCH 13/26] Try to disable ncurses dep for libedit (#615) I'm shamelessly copying what [AlmaLinux does](https://git.almalinux.org/rpms/libedit/src/commit/3f0893c4cd8e0cbb2f556d2fad48326c9c037a6c/SPECS/libedit.spec#L44-L48), so let's try that... Closes #613 --- cmake/wasi-sdk-toolchain.cmake | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index ef977c3..d433b9c 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -109,6 +109,22 @@ if(WASI_SDK_LLDB) -DLLDB_ENABLE_LINUXPTY=OFF ) + set(extra_configure_commands) + if(CMAKE_SYSTEM_NAME STREQUAL Linux) + set(extra_configure_commands + # By default it looks like `libedit` tries to link to `libncurses.so` and + # such on Linux. This is problematic as systems may not have that + # installed. Turns out though at least for AlmaLinux [1] they just edit + # makefile and pkg-config info and it works out. Who knew! I thought + # one of the millions of lines in `./configure` would take care of this + # but apparently we're still resorting to editing things raw... + # + # [1]: https://git.almalinux.org/rpms/libedit/src/commit/3f0893c4cd8e0cbb2f556d2fad48326c9c037a6c/SPECS/libedit.spec#L44-L48 + COMMAND sed -i "s/lncurses/ltinfo/" src/Makefile + COMMAND sed -i "s/ -lncurses//" libedit.pc + ) + endif() + if (WASI_SDK_LIBEDIT) include(ProcessorCount) ProcessorCount(nproc) @@ -129,10 +145,13 @@ if(WASI_SDK_LLDB) --prefix=${wasi_tmp_install} --enable-pic --disable-examples + --disable-static + --disable-silent-rules CC=${CMAKE_C_COMPILER} LDFLAGS=${libedit_ldflags} + ${extra_configure_commands} BUILD_COMMAND - ${MAKE_EXECUTABLE} -j${nproc} V=1 + ${MAKE_EXECUTABLE} -j${nproc} USES_TERMINAL_CONFIGURE ON USES_TERMINAL_BUILD ON From 249054e5427023344c1906afde0e9073714d1930 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 16 Mar 2026 12:41:12 -0500 Subject: [PATCH 14/26] Don't use `lib64` dir for libxml2 (#617) Currently LLDB binaries as-is don't work, and this should fix them. --- cmake/wasi-sdk-toolchain.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index d433b9c..5468d93 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -173,6 +173,12 @@ if(WASI_SDK_LLDB) list(APPEND libxml_cmake_args -DLIBXML2_WITH_ICONV=OFF) endif() + # Our AlmaLinux:8 container ends up using `lib64` instead of `lib` by default + # which doesn't match LLVM, so specifically use the same dir as LLVM. + if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + list(APPEND libxml_cmake_args -DCMAKE_INSTALL_LIBDIR=lib) + endif() + if (WASI_SDK_LIBXML2) ExternalProject_Add(libxml2 URL https://download.gnome.org/sources/libxml2/2.15/libxml2-2.15.2.tar.xz From 62a1a88c3dc2ced6e848b63ebe7fcf9928e6bf87 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Apr 2026 12:40:27 -0500 Subject: [PATCH 15/26] Update wasm-component-ld (#623) Keeping it up-to-date --- cmake/wasi-sdk-toolchain.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 5468d93..57a4b4b 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -261,7 +261,7 @@ install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_ins # Build logic for `wasm-component-ld` installed from Rust code. set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld) set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX}) -set(wasm_component_ld_version 0.5.21) +set(wasm_component_ld_version 0.5.22) if(RUST_TARGET) set(rust_target_flag --target=${RUST_TARGET}) endif() From 42b7263534b273dcb9b43310cb34ef5ef347a767 Mon Sep 17 00:00:00 2001 From: Bruno Verachten Date: Wed, 8 Apr 2026 16:42:04 +0200 Subject: [PATCH 16/26] ci: add riscv64-linux build via cross-compilation on ubuntu-24.04 (#621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework of the riscv64-linux CI build to use CMake cross-compilation on a standard `ubuntu-24.04` runner, rather than a native RISE runner. ## What changed **`ci/docker/Dockerfile.riscv64-linux`** (new): - Ubuntu 24.04 base — has `crossbuild-essential-riscv64` in its package repos - Sets `CC=riscv64-linux-gnu-gcc` / `CXX=riscv64-linux-gnu-g++` so CMake detects cross-compilation and causes LLVM to build a native `llvm-tblgen` first, then cross-compile the rest of the toolchain - Sets `CARGO_TARGET_RISCV64_UNKNOWN_LINUX_GNU_LINKER` for Rust cross-builds - `XDG_CACHE_HOME=/tmp/cache` avoids write permission issues in the container **`ci/docker-build.sh`**: - Select `ci/docker/Dockerfile.` if it exists, fall back to the default `ci/docker/Dockerfile` - Make the wasmtime volume mount conditional on `WASI_SDK_CI_SKIP_SYSROOT != 1` **`.github/workflows/main.yml`**: - New `riscv64-linux` matrix entry: `os: ubuntu-24.04`, `rust_target: riscv64-unknown-linux-gnu` - `cross_cmake_args: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=riscv64 -DWASI_SDK_LLDB=OFF` - `WASI_SDK_CI_SKIP_SYSROOT: 1` - Handle `cross_cmake_args` in the cmake flags step ## Why WASI_SDK_CI_SKIP_SYSROOT The cross-compiled clang runs on riscv64, not on the x86_64 build host, so the wasm sysroot step is skipped. ## Why WASI_SDK_LLDB=OFF Avoids cross-compiling libedit and libxml2 in this first iteration; can be re-enabled as a follow-up. Closes #607 --------- Signed-off-by: Bruno Verachten --- .github/workflows/main.yml | 18 ++++++++++++++++++ ci/docker-build.sh | 15 +++++++++++---- ci/docker/Dockerfile.riscv64-linux | 28 ++++++++++++++++++++++++++++ ci/merge-artifacts.sh | 5 +++-- 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 ci/docker/Dockerfile.riscv64-linux diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14d51b5..0262f5f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,6 +28,21 @@ jobs: - artifact: arm64-linux os: ubuntu-22.04-arm + - artifact: riscv64-linux + os: ubuntu-24.04 + rust_target: riscv64gc-unknown-linux-gnu + cross_cmake_args: >- + -DCMAKE_SYSTEM_NAME=Linux + -DCMAKE_SYSTEM_PROCESSOR=riscv64 + -DCMAKE_C_COMPILER=riscv64-linux-gnu-gcc + -DCMAKE_CXX_COMPILER=riscv64-linux-gnu-g++ + -DWASI_SDK_LLDB=OFF + env: + WASI_SDK_CI_SKIP_SYSROOT: 1 + WASI_SDK_CI_TOOLCHAIN_LLVM_CMAKE_ARGS: >- + -DCMAKE_SYSTEM_NAME=Linux + -DCMAKE_SYSTEM_PROCESSOR=riscv64 + - artifact: arm64-macos os: macos-14 rust_target: aarch64-apple-darwin @@ -85,6 +100,9 @@ jobs: rustup target add ${{ matrix.rust_target }} cmake_args="$cmake_args -DRUST_TARGET=${{ matrix.rust_target }}" fi + if [ "${{ matrix.cross_cmake_args }}" != "" ]; then + cmake_args="$cmake_args ${{ matrix.cross_cmake_args }}" + fi echo WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS="$cmake_args" >> $GITHUB_ENV shell: bash diff --git a/ci/docker-build.sh b/ci/docker-build.sh index 7773f79..2915e28 100755 --- a/ci/docker-build.sh +++ b/ci/docker-build.sh @@ -15,8 +15,13 @@ fi set -x -# Build the Docker imager -docker build --tag wasi-sdk-builder ci/docker +# Build the Docker image. Use an artifact-specific Dockerfile if one exists +# (e.g. ci/docker/Dockerfile.riscv64-linux), otherwise use the default. +dockerfile=ci/docker/Dockerfile +if [ -f "ci/docker/Dockerfile.$1" ]; then + dockerfile="ci/docker/Dockerfile.$1" +fi +docker build --tag wasi-sdk-builder --file "$dockerfile" ci/docker # Perform the build in `/src`. The current directory is mounted read-write at # this location as well. To ensure that container-created files are reasonable @@ -34,9 +39,11 @@ args="$args --volume $ccache_dir:/ccache:Z --env CCACHE_DIR=/ccache" # Inherit some tools from the host into this container. This ensures that the # decision made on CI of what versions to use is the canonical source of truth -# for theset ools +# for these tools. args="$args --volume `rustc --print sysroot`:/rustc:ro" -args="$args --volume $(dirname $(which wasmtime)):/wasmtime:ro" +if [ "${WASI_SDK_CI_SKIP_SYSROOT:-}" != "1" ]; then + args="$args --volume $(dirname $(command -v wasmtime)):/wasmtime:ro" +fi # Pass through some env vars that `build.sh` reads args="$args --env WASI_SDK_CI_TOOLCHAIN_CMAKE_ARGS" diff --git a/ci/docker/Dockerfile.riscv64-linux b/ci/docker/Dockerfile.riscv64-linux new file mode 100644 index 0000000..f4456ec --- /dev/null +++ b/ci/docker/Dockerfile.riscv64-linux @@ -0,0 +1,28 @@ +# Ubuntu 24.04 is used here (rather than AlmaLinux 8) because it has +# riscv64 cross-compilation packages in its repositories. +FROM ubuntu:24.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl \ + ca-certificates \ + crossbuild-essential-riscv64 \ + clang \ + lld \ + python3 \ + git \ + unzip \ + cmake \ + ninja-build \ + ccache \ + && rm -rf /var/lib/apt/lists/* + +# Cargo needs an explicit linker when cross-compiling for riscv64. +# The C/C++ cross-compiler is passed via CMAKE_C/CXX_COMPILER cmake flags +# rather than CC/CXX env vars so that LLVM's native tblgen sub-build can +# still find the host compiler (cmake cache vars are not inherited by +# subprocess cmake invocations, but env vars are). +ENV CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc + +# Tell programs to cache in a location that both isn't a `--volume` mounted root +# and isn't `/root` in the container as that won't be writable during the build. +ENV XDG_CACHE_HOME=/tmp/cache diff --git a/ci/merge-artifacts.sh b/ci/merge-artifacts.sh index e64b68e..9a1e683 100755 --- a/ci/merge-artifacts.sh +++ b/ci/merge-artifacts.sh @@ -21,8 +21,9 @@ make_deb() { fi case $build in - dist-x86_64-linux) deb_arch=amd64 ;; - dist-arm64-linux) deb_arch=arm64 ;; + dist-x86_64-linux) deb_arch=amd64 ;; + dist-arm64-linux) deb_arch=arm64 ;; + dist-riscv64-linux) deb_arch=riscv64 ;; *) echo "unknown build $build" exit 1 From 003cf14969ecca789c1922f9047e9a31872e9b52 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 8 Apr 2026 17:45:31 -0500 Subject: [PATCH 17/26] Build a sysroot that supports C++ exceptions by default (#606) This commit collects together some LLVM PRs, some changes in the build configuration here, and some thoughts from #565 and related issues. Specifically the changes here are: * The patch for llvm/llvm-project#168449 is updated to its upstream (unlanded) form. * Patches for the (landed) llvm/llvm-project#185770 and llvm/llvm-project#185775 are added. * The `WASI_SDK_EXCEPTIONS` configuration is now either `ON`, `OFF`, or `DUAL`. The default depends on the version of Clang in use, where 23.0.0+ (which isn't released officially yet) will be `DUAL` and otherwise it's `OFF`. CI for our custom-built patched toolchain defaults to `DUAL`. * In `DUAL` mode libcxx is built twice into two different directories, once with exceptions and once without. This is supported by LLVM patches and means that Clang will select the right set of libraries based on compiler flags. The end result here is that the produced toolchain from this repository, by default, supports C++ exceptions. Additionally if exceptions-related flags are not passed then the final binary will not use C++ exceptions nor require the wasm exception-handling proposal. There's still follow-up work from #565, such as: * Subjectively it feels wordy to pass `-fwasm-exceptions` vs `-fexceptions`. * Personally I think `-mllvm -wasm-use-legacy-eh=false` should become the default upstream. * Subjectively I don't think that `-lunwind` should be necessary and it should be injected automatically with `-fwasm-exceptions` (or `-fexceptions`). * Shared libraries for exceptions remain disabled due to build errors I do not personally know how to resolve. I'll file follow-up issues for these once this has landed since they're more minor compared to the main body of "anything works". Closes #334 Closes #565 --------- Co-authored-by: Joel Dice --- .github/workflows/main.yml | 10 ++- CppExceptions.md | 78 +++++++++-------- README.md | 4 +- ci/build.sh | 1 + cmake/wasi-sdk-sysroot.cmake | 79 +++++++++++++---- cmake/wasi-sdk-toolchain.cmake | 3 + src/llvm-pr-168449.patch | 27 +++--- src/llvm-pr-185770.patch | 105 +++++++++++++++++++++++ src/llvm-pr-185775.patch | 152 +++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 4 +- 10 files changed, 391 insertions(+), 72 deletions(-) create mode 100644 src/llvm-pr-185770.patch create mode 100644 src/llvm-pr-185775.patch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0262f5f..2644c8f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -188,10 +188,16 @@ jobs: - uses: ./.github/actions/checkout - uses: ./.github/actions/install-deps - run: cargo install wasm-component-ld@0.5.21 - - run: sudo apt-get update -y && sudo apt-get install -y clang-20 lld-20 + - name: Install LLVM 22 + run: | + v=22 + rel=$(lsb_release -cs) + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/keyrings/llvm.asc + echo "deb [signed-by=/etc/apt/keyrings/llvm.asc] http://apt.llvm.org/$rel/ llvm-toolchain-$rel-$v main" | sudo tee /etc/apt/sources.list.d/llvm-$v.list + sudo apt-get update -y && sudo apt-get install -y clang-$v lld-$v - run: | cmake -G Ninja -B build -S . \ - -DCMAKE_C_COMPILER=/usr/lib/llvm-20/bin/clang \ + -DCMAKE_C_COMPILER=/usr/lib/llvm-22/bin/clang \ -DCMAKE_SYSTEM_NAME=WASI \ -DWASI_SDK_INCLUDE_TESTS=ON \ -DWASI_SDK_CPU_CFLAGS="" \ diff --git a/CppExceptions.md b/CppExceptions.md index 7ea1e2d..1ca79bf 100644 --- a/CppExceptions.md +++ b/CppExceptions.md @@ -1,25 +1,17 @@ # Support for C++ Exceptions -The released artifacts for wasi-sdk at this time do not support C++ exceptions. -LLVM and Clang, however, have support for C++ exceptions in WebAssembly and this -is intended to serve as documentation of the current state of affairs of using -C++ exceptions. It should be noted though that the current status of C++ -exceptions support is not intended to be the final state of support, and this is -all continuing to be iterated on over time. +> **Note**: this documentation does not cover wasi-sdk-31, the latest version +> of wasi-sdk at this time. -## Building wasi-sdk with exceptions +From wasi-sdk-33 and onwards the artifacts produced by this repository support +compiling C++ code both with and without exceptions. The sysroot for wasm +targets contains two copies of the C++ standard library and headers -- one with +exceptions enabled and one with exceptions disabled. These are automatically +selected based on compilation flags. This means that wasi-sdk-produced binaries +can avoid using wasm exceptions entirely by disabling C++ exceptions, or C++ +exceptions can be enabled in which case wasm exceptions will be used. -When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON` -to enable support for C++ exceptions. For example: - -```shell script -$ cmake -G Ninja -B build/sysroot -S . \ - -DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \ - -DWASI_SDK_EXCEPTIONS=ON -``` - -The C++ standard library will be compiled with support for exceptions for the -desired targets and the resulting sysroot supports using exceptions. +Currently the default is for C++ exceptions to be disabled. ## Compiling code with C++ exceptions @@ -36,25 +28,43 @@ This can be specified for example with: ```shell script $ export CFLAGS="-fwasm-exceptions -mllvm -wasm-use-legacy-eh=false" -$ export LDFLAGS="-lunwind" +$ export LDFLAGS="-fwasm-exceptions -lunwind" ``` -## Limitations +Note that `-fwasm-exceptions` must be present when linking to select the +correct C++ standard library to link. + +## Building wasi-sdk with exceptions -Currently C++ exceptions support in wasi-sdk does not support shared libraries. -Fixing this will require resolving some miscellaneous build issues in this -repository itself. +When building the sysroot with wasi-sdk you can pass `-DWASI_SDK_EXCEPTIONS=ON` +to enable support for C++ exceptions. For example: -## Future Plans +```shell script +$ cmake -G Ninja -B build/sysroot -S . \ + -DCMAKE_TOOLCHAIN_FILE=$path/to/wasi-sdk-p1.cmake \ + -DWASI_SDK_EXCEPTIONS=ON +``` + +The C++ standard library will be compiled with support for exceptions for the +desired targets and the resulting sysroot supports using exceptions. Note that +enabling C++ exceptions requires LLVM 22 or later. + +C++ exceptions are disabled by default for local builds. With a future release +of LLVM 23 the dual-sysroot nature will be on-by-default. + +## Limitations -There are a few tracking issues with historical discussion about C++ exceptions -support in wasi-sdk such as [#334](https://github.com/WebAssembly/wasi-sdk/issues/334) -and [#565](https://github.com/WebAssembly/wasi-sdk/issues/565). The major -remaining items are: +There are a few known limitations/bugs/todos around exceptions support in +wasi-sdk at this time: -* Figure out support for shared libraries. -* Determine how to ship a sysroot that supports both with-and-without - exceptions. -* Figure out how to avoid the need for extra compiler flags when using - exceptions. -* Figure out if a new wasm target is warranted. +* Currently C++ exceptions support in wasi-sdk does not support shared + libraries. Fixing this will require resolving some miscellaneous build + issues in this repository itself as well as [resolving some upstream + issues](https://github.com/llvm/llvm-project/issues/188077). +* Currently `-fwasm-exceptions` is a required flag to enable C++ exceptions. + It's unclear whether `-fexceptions` should also be supported as a substitute. +* Currently LLVM defaults to using the legacy exception-handling proposal and + this will likely change in the future. Precompiled libraries for wasi-sdk are + all built with the standard exception-handling proposal. +* Currently `-lunwind` is required when linking, but this may become automatic + in the future. diff --git a/README.md b/README.md index 7d28f69..7893c96 100644 --- a/README.md +++ b/README.md @@ -209,8 +209,8 @@ disabled in a configure step before building with WASI SDK. ## Notable Limitations -* C++ exceptions are disabled by default. For more information see - [CppExceptions.md]. +* C++ exceptions are disabled by default and require extra configuration to get + working, see [CppExceptions.md]. * C `setjmp`/`longjmp` require some extra configuration to get working, see [SetjmpLongjmp.md]. * Most targets do not support spawning a thread. Experimental support for diff --git a/ci/build.sh b/ci/build.sh index 7525aa4..7c02fff 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -37,6 +37,7 @@ cmake -G Ninja -B $build_dir/sysroot -S . \ -DCMAKE_C_COMPILER_WORKS=ON \ -DCMAKE_CXX_COMPILER_WORKS=ON \ -DWASI_SDK_INCLUDE_TESTS=ON \ + -DWASI_SDK_EXCEPTIONS=DUAL \ "-DCMAKE_INSTALL_PREFIX=$build_dir/install" ninja -C $build_dir/sysroot install dist -v diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 38ed426..491180e 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -20,13 +20,28 @@ message(STATUS "Found executable for `ar`: ${CMAKE_AR}") find_program(MAKE make REQUIRED) +set(EXCEPTIONS_DEFAULT "OFF") +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 23.0.0) + set(EXCEPTIONS_DEFAULT "DUAL") +endif() + option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" ON) option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF) option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF) option(WASI_SDK_LTO "Whether or not to build LTO assets" ON) -option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF) +set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled") set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable") +if ((WASI_SDK_EXCEPTIONS STREQUAL "DUAL") OR (WASI_SDK_EXCEPTIONS STREQUAL "ON")) + if(CMAKE_C_COMPILER_VERSION VERSION_LESS 22.0.0) + message(FATAL_ERROR "enabling C++ exceptions requires Clang 22 or later") + endif() +elseif(WASI_SDK_EXCEPTIONS STREQUAL "OFF") + # No extra validation needed +else() + message(FATAL_ERROR "unknown WASI_SDK_EXCEPTIONS value ${WASI_SDK_EXCEPTIONS}, expected one of: OFF, ON, DUAL") +endif() + set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) set(wasi_sysroot ${wasi_tmp_install}/share/wasi-sysroot) set(wasi_resource_dir ${wasi_tmp_install}/wasi-resource-dir) @@ -225,7 +240,7 @@ execute_process( OUTPUT_VARIABLE llvm_version OUTPUT_STRIP_TRAILING_WHITESPACE) -function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix) +function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions) if(${target} MATCHES threads) set(pic OFF) set(target_flags -pthread) @@ -251,7 +266,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ --sysroot ${wasi_sysroot} -resource-dir ${wasi_resource_dir}) - if (WASI_SDK_EXCEPTIONS) + set(exnsuffix "") + + if (exceptions) # TODO: lots of builds fail with shared libraries and `-fPIC`. Looks like # things are maybe changing in llvm/llvm-project#159143 but otherwise I'm at # least not really sure what the state of shared libraries and exceptions @@ -260,6 +277,13 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ set(pic OFF) set(runtimes "libunwind;${runtimes}") list(APPEND extra_flags -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + set(exnsuffix "/eh") + endif() + else() + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + set(exnsuffix "/noeh") + endif() endif() # The `wasm32-wasi` target is deprecated in clang, so ignore the deprecation @@ -279,7 +303,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ ${default_cmake_args} # Ensure headers are installed in a target-specific path instead of a # target-generic path. - -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target} + -DCMAKE_INSTALL_INCLUDEDIR=${wasi_sysroot}/include/${target}${exnsuffix} -DCMAKE_STAGING_PREFIX=${wasi_sysroot} -DCMAKE_POSITION_INDEPENDENT_CODE=${pic} -DLIBCXX_ENABLE_THREADS:BOOL=ON @@ -288,20 +312,20 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF -DLLVM_COMPILER_CHECKED=ON -DLIBCXX_ENABLE_SHARED:BOOL=${pic} - -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions} -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF -DLIBCXX_ABI_VERSION=2 - -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions} -DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON -DLIBCXXABI_ENABLE_THREADS:BOOL=ON -DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF - -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions} -DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} -DLIBUNWIND_ENABLE_THREADS:BOOL=ON -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON @@ -310,9 +334,9 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DCMAKE_C_FLAGS=${extra_cflags} -DCMAKE_ASM_FLAGS=${extra_cflags} -DCMAKE_CXX_FLAGS=${extra_cxxflags} - -DLIBCXX_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} - -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} - -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${extra_libdir_suffix} + -DLIBCXX_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBCXXABI_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} + -DLIBUNWIND_LIBDIR_SUFFIX=/${target}${exnsuffix}${extra_libdir_suffix} -DLIBCXX_INCLUDE_TESTS=OFF -DLIBCXX_INCLUDE_BENCHMARKS=OFF @@ -327,21 +351,45 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ USES_TERMINAL_CONFIGURE ON USES_TERMINAL_BUILD ON USES_TERMINAL_INSTALL ON + USES_TERMINAL_PATCH ON PATCH_COMMAND ${CMAKE_COMMAND} -E chdir .. bash -c "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-168449.patch -R --check" COMMAND ${CMAKE_COMMAND} -E chdir .. bash -c "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-186054.patch -R --check" + COMMAND + ${CMAKE_COMMAND} -E chdir .. bash -c + "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185770.patch -R --check" ) + add_dependencies(libcxx-${target} libcxx-${target}${target_suffix}-build) endfunction() -function(define_libcxx target) - define_libcxx_sub(${target} "" "" "") - if(WASI_SDK_LTO) +function(define_libcxx_and_lto target target_suffix exceptions) + define_libcxx_sub(${target} "${target_suffix}" "" "" ${exceptions}) + if (WASI_SDK_LTO) # Note: clang knows this /llvm-lto/${llvm_version} convention. # https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/clang/lib/Driver/ToolChains/WebAssembly.cpp#L204-L210 - define_libcxx_sub(${target} "-lto" "-flto=full" "/llvm-lto/${llvm_version}") + define_libcxx_sub(${target} ${target_suffix}-lto "-flto=full" "/llvm-lto/${llvm_version}" ${exceptions}) + endif() +endfunction() + +function(define_libcxx target) + add_custom_target(libcxx-${target}) + + # For dual-mode exceptions-and-not there are two versions of libcxx which are + # compiled and placed into the sysroot. They're named slightly differently to + # have unique CMake rules. + # + # Otherwise there's only one build of libcxx and it's either got exceptions or + # it doesn't depending on configuration. + if (WASI_SDK_EXCEPTIONS STREQUAL "DUAL") + define_libcxx_and_lto(${target} "" OFF) + define_libcxx_and_lto(${target} "-exn" ON) + elseif(WASI_SDK_EXCEPTIONS STREQUAL "ON") + define_libcxx_and_lto(${target} "" ON) + else() + define_libcxx_and_lto(${target} "" OFF) endif() # As of this writing, `clang++` will ignore the target-specific include dirs @@ -349,8 +397,7 @@ function(define_libcxx target) add_custom_target(libcxx-${target}-extra-dir COMMAND ${CMAKE_COMMAND} -E make_directory ${wasi_sysroot}/include/c++/v1 COMMENT "creating libcxx-specific header file folder") - add_custom_target(libcxx-${target} - DEPENDS libcxx-${target}-build $<$:libcxx-${target}-lto-build> libcxx-${target}-extra-dir) + add_dependencies(libcxx-${target} libcxx-${target}-extra-dir) endfunction() foreach(target IN LISTS WASI_SDK_TARGETS) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 57a4b4b..7b05a98 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -247,6 +247,9 @@ ExternalProject_Add(llvm-build USES_TERMINAL_CONFIGURE ON USES_TERMINAL_BUILD ON USES_TERMINAL_INSTALL ON + PATCH_COMMAND + ${CMAKE_COMMAND} -E chdir .. bash -c + "git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch || git apply ${CMAKE_SOURCE_DIR}/src/llvm-pr-185775.patch -R --check" ) add_custom_target(build ALL DEPENDS llvm-build) diff --git a/src/llvm-pr-168449.patch b/src/llvm-pr-168449.patch index a0da1d8..f5b8738 100644 --- a/src/llvm-pr-168449.patch +++ b/src/llvm-pr-168449.patch @@ -1,22 +1,17 @@ -diff --git a/libunwind/src/assembly.h b/libunwind/src/assembly.h -index f8e83e138eff..c5097d25b0c6 100644 ---- a/libunwind/src/assembly.h -+++ b/libunwind/src/assembly.h -@@ -249,6 +249,9 @@ aliasname: \ - #define WEAK_ALIAS(name, aliasname) - #define NO_EXEC_STACK_DIRECTIVE - -+#elif defined(__wasm__) -+#define NO_EXEC_STACK_DIRECTIVE -+ - // clang-format on - #else - +From 852c8a2ebc0fdb1e781591e3e6e08d3a539bcfc3 Mon Sep 17 00:00:00 2001 +From: Yerzhan Zhamashev +Date: Wed, 21 Jan 2026 16:50:41 +0200 +Subject: [PATCH] libunwind: exclude __declspec from wasm build + +--- + libunwind/src/config.h | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + diff --git a/libunwind/src/config.h b/libunwind/src/config.h -index deb5a4d4d73d..23c9f012cbcf 100644 +index f017403fa2234..6014a37e27212 100644 --- a/libunwind/src/config.h +++ b/libunwind/src/config.h -@@ -66,7 +66,8 @@ +@@ -75,7 +75,8 @@ #define _LIBUNWIND_EXPORT #define _LIBUNWIND_HIDDEN #else diff --git a/src/llvm-pr-185770.patch b/src/llvm-pr-185770.patch new file mode 100644 index 0000000..2d47923 --- /dev/null +++ b/src/llvm-pr-185770.patch @@ -0,0 +1,105 @@ +From d702761d9135ebbb83590d4dd1323be433701ebd Mon Sep 17 00:00:00 2001 +From: Alex Crichton +Date: Tue, 10 Mar 2026 15:49:55 -0700 +Subject: [PATCH] [WebAssembly] Move __cpp_exception to libunwind + +The `__cpp_exception` symbol is now defined in libunwind instead of +compiler-rt. This is moved for a few reasons, but the primary reason is +that compiler-rt is linked duplicate-ly into all shared objects meaning +that it's not suitable for define-once symbols such as +`__cpp_exception`. By moving the definition to the user of the symbol, +libunwind itself, that guarantees that the symbol should be defined +exactly once and only when appropriate. A secondary reason for this +movement is that it avoids the need to compile compiler-rt twice: once +with exception and once without, and instead the same build can be used +for both exceptions-and-not. +--- + compiler-rt/lib/builtins/CMakeLists.txt | 1 - + .../lib/builtins/wasm/__cpp_exception.S | 26 ------------------- + libunwind/src/Unwind-wasm.c | 15 +++++++++++ + .../compiler-rt/lib/builtins/sources.gni | 1 - + 4 files changed, 15 insertions(+), 28 deletions(-) + delete mode 100644 compiler-rt/lib/builtins/wasm/__cpp_exception.S + +diff --git a/compiler-rt/lib/builtins/CMakeLists.txt b/compiler-rt/lib/builtins/CMakeLists.txt +index 6c27f6d4d529e..f0570a9092f40 100644 +--- a/compiler-rt/lib/builtins/CMakeLists.txt ++++ b/compiler-rt/lib/builtins/CMakeLists.txt +@@ -891,7 +891,6 @@ set(s390x_SOURCES + + set(wasm_SOURCES + wasm/__c_longjmp.S +- wasm/__cpp_exception.S + ${GENERIC_TF_SOURCES} + ${GENERIC_SOURCES} + ) +diff --git a/compiler-rt/lib/builtins/wasm/__cpp_exception.S b/compiler-rt/lib/builtins/wasm/__cpp_exception.S +deleted file mode 100644 +index 0496e1dbf6158..0000000000000 +--- a/compiler-rt/lib/builtins/wasm/__cpp_exception.S ++++ /dev/null +@@ -1,26 +0,0 @@ +-//===-- __cpp_exception.S - Implement __cpp_exception ---------------------===// +-// +-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +-// See https://llvm.org/LICENSE.txt for license information. +-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +-// +-//===----------------------------------------------------------------------===// +-// +-// This file implements __cpp_exception which LLVM uses to implement exception +-// handling when Wasm EH is enabled. +-// +-//===----------------------------------------------------------------------===// +- +-#ifdef __wasm_exception_handling__ +- +-#ifdef __wasm64__ +-#define PTR i64 +-#else +-#define PTR i32 +-#endif +- +-.globl __cpp_exception +-.tagtype __cpp_exception PTR +-__cpp_exception: +- +-#endif // !__wasm_exception_handling__ +diff --git a/libunwind/src/Unwind-wasm.c b/libunwind/src/Unwind-wasm.c +index 2f4498c3f3989..c0ca9b775d244 100644 +--- a/libunwind/src/Unwind-wasm.c ++++ b/libunwind/src/Unwind-wasm.c +@@ -69,6 +69,21 @@ _Unwind_RaiseException(_Unwind_Exception *exception_object) { + __builtin_wasm_throw(0, exception_object); + } + ++// Define the `__cpp_exception` symbol which `__builtin_wasm_throw` above will ++// reference. This is defined here in `libunwind` as the single canonical ++// definition for this API and it's required for users to ensure that there's ++// only one copy of `libunwind` within a wasm module to ensure this is only ++// defined once and exactly once. ++__asm__(".globl __cpp_exception\n" ++#if defined(__wasm32__) ++ ".tagtype __cpp_exception i32\n" ++#elif defined(__wasm64__) ++ ".tagtype __cpp_exception i64\n" ++#else ++#error "Unsupported Wasm architecture" ++#endif ++ "__cpp_exception:\n"); ++ + /// Called by __cxa_end_catch. + _LIBUNWIND_EXPORT void + _Unwind_DeleteException(_Unwind_Exception *exception_object) { +diff --git a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni +index 2ac71aa8e8367..c9eeede16e3eb 100644 +--- a/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni ++++ b/llvm/utils/gn/secondary/compiler-rt/lib/builtins/sources.gni +@@ -539,7 +539,6 @@ if (current_cpu == "ve") { + if (current_cpu == "wasm") { + builtins_sources += [ + "wasm/__c_longjmp.S", +- "wasm/__cpp_exception.S", + ] + } + diff --git a/src/llvm-pr-185775.patch b/src/llvm-pr-185775.patch new file mode 100644 index 0000000..f1fdb6a --- /dev/null +++ b/src/llvm-pr-185775.patch @@ -0,0 +1,152 @@ +From 0e36e8f304cd5f3997916f5d85201bb17e340337 Mon Sep 17 00:00:00 2001 +From: Alex Crichton +Date: Tue, 10 Mar 2026 16:14:36 -0700 +Subject: [PATCH] [WebAssembly] Clang support for exception-based lookup paths + +This commit is an attempt to make progress on WebAssembly/wasi-sdk#565 +where with wasi-sdk I'd like to ship a single toolchain which is +capable of building binaries both with C++ exceptions and without. This +means that there can't be a single set of precompiled libraries that are +used because one set of libraries is wrong for the other mode. The +support added here is to use `-fwasm-exceptions` to automatically select +a lookup path in the sysroot. The intention is then that wasi-sdk will +ship both a "eh" set of C++ libraries as well as a "noeh" set of C++ +libraries too. Clang will automatically select the correct one based on +compilation flags which means that the final distribution will be able +to build both binaries with exceptions and without. +--- + clang/lib/Driver/ToolChains/WebAssembly.cpp | 51 ++++++++++++++------- + clang/test/Driver/wasm-toolchain.cpp | 35 ++++++++++++++ + 2 files changed, 70 insertions(+), 16 deletions(-) + +diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp +index b5fa5760a46a0..e532ef0743cc2 100644 +--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp ++++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp +@@ -34,6 +34,15 @@ std::string WebAssembly::getMultiarchTriple(const Driver &D, + TargetTriple.getOSAndEnvironmentName()).str(); + } + ++/// Returns a directory name in which separate objects compile with/without ++/// exceptions may lie. This is used both for `#include` paths as well as lib ++/// paths. ++static std::string GetCXXExceptionsDir(const ArgList &DriverArgs) { ++ if (DriverArgs.getLastArg(options::OPT_fwasm_exceptions)) ++ return "eh"; ++ return "noeh"; ++} ++ + std::string wasm::Linker::getLinkerPath(const ArgList &Args) const { + const ToolChain &ToolChain = getToolChain(); + if (const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ)) { +@@ -230,12 +239,16 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, + } + } + +-/// Given a base library directory, append path components to form the +-/// LTO directory. +-static std::string AppendLTOLibDir(const std::string &Dir) { ++/// Append `Dir` to `Paths`, but also include the LTO directories before that if ++/// LTO is eanbled. ++static void AppendLibDirAndLTODir(ToolChain::path_list &Paths, const Driver &D, ++ const std::string &Dir) { ++ if (D.isUsingLTO()) { + // The version allows the path to be keyed to the specific version of + // LLVM in used, as the bitcode format is not stable. +- return Dir + "/llvm-lto/" LLVM_VERSION_STRING; ++ Paths.push_back(Dir + "/llvm-lto/" LLVM_VERSION_STRING); ++ } ++ Paths.push_back(Dir); + } + + WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple, +@@ -256,14 +269,15 @@ WebAssembly::WebAssembly(const Driver &D, const llvm::Triple &Triple, + } else { + const std::string MultiarchTriple = + getMultiarchTriple(getDriver(), Triple, SysRoot); +- if (D.isUsingLTO()) { +- // For LTO, enable use of lto-enabled sysroot libraries too, if available. +- // Note that the directory is keyed to the LLVM revision, as LLVM's +- // bitcode format is not stable. +- auto Dir = AppendLTOLibDir(SysRoot + "/lib/" + MultiarchTriple); +- getFilePaths().push_back(Dir); +- } +- getFilePaths().push_back(SysRoot + "/lib/" + MultiarchTriple); ++ std::string TripleLibDir = SysRoot + "/lib/" + MultiarchTriple; ++ // Allow sysroots to segregate objects based on whether exceptions are ++ // enabled or not. This is intended to assist with distribution of pre-built ++ // sysroots that contain libraries that are capable of producing binaries ++ // entirely without exception-handling instructions but also with if ++ // exceptions are enabled, for example. ++ AppendLibDirAndLTODir(getFilePaths(), D, ++ TripleLibDir + "/" + GetCXXExceptionsDir(Args)); ++ AppendLibDirAndLTODir(getFilePaths(), D, TripleLibDir); + } + + if (getTriple().getOS() == llvm::Triple::WASI) { +@@ -580,13 +594,18 @@ void WebAssembly::addLibCxxIncludePaths( + if (Version.empty()) + return; + +- // First add the per-target include path if the OS is known. ++ // First add the per-target-per-exception-handling include path if the ++ // OS is known, then second add the per-target include path. + if (IsKnownOs) { +- std::string TargetDir = LibPath + "/" + MultiarchTriple + "/c++/" + Version; +- addSystemInclude(DriverArgs, CC1Args, TargetDir); ++ std::string TargetDir = LibPath + "/" + MultiarchTriple; ++ std::string Suffix = "/c++/" + Version; ++ addSystemInclude(DriverArgs, CC1Args, ++ TargetDir + "/" + GetCXXExceptionsDir(DriverArgs) + ++ Suffix); ++ addSystemInclude(DriverArgs, CC1Args, TargetDir + Suffix); + } + +- // Second add the generic one. ++ // Third add the generic one. + addSystemInclude(DriverArgs, CC1Args, LibPath + "/c++/" + Version); + } + +diff --git a/clang/test/Driver/wasm-toolchain.cpp b/clang/test/Driver/wasm-toolchain.cpp +index d7ff76cedfd10..30a2f9397e3f4 100644 +--- a/clang/test/Driver/wasm-toolchain.cpp ++++ b/clang/test/Driver/wasm-toolchain.cpp +@@ -111,3 +111,38 @@ + // COMPILE_WALI_STDCXX: "-internal-isystem" "[[RESOURCE_DIR]]{{(/|\\\\)}}include" + // COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-linux-muslwali" + // COMPILE_WALI_STDCXX: "-internal-isystem" "[[SYSROOT:[^"]+]]/include" ++ ++// With a known OS "eh" and "noeh" directories are added to enable segregating ++// object built with/without exception-handling ++ ++// RUN: %clangxx -### --target=wasm32-wasi --stdlib=libc++ %s 2>&1 \ ++// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \ ++// RUN: | FileCheck -check-prefix=EH_OFF %s ++// EH_OFF: "-cc1" ++// EH_OFF: "-isysroot" "[[SYSROOT:[^"]+]]" ++// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1" ++// EH_OFF-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1" ++// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1" ++// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1" ++// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi" ++// EH_OFF: "-internal-isystem" "[[SYSROOT:[^"]+]]/include" ++ ++// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --stdlib=libc++ %s 2>&1 \ ++// RUN: --sysroot=%S/Inputs/basic_linux_libcxx_tree/usr \ ++// RUN: | FileCheck -check-prefix=EH_ON %s ++// EH_ON: "-cc1" ++// EH_ON: "-isysroot" "[[SYSROOT:[^"]+]]" ++// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/eh/c++/v1" ++// EH_ON-NOT: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/noeh/c++/v1" ++// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi/c++/v1" ++// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/c++/v1" ++// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include/wasm32-wasi" ++// EH_ON: "-internal-isystem" "[[SYSROOT:[^"]+]]/include" ++// ++// RUN: %clangxx -### --target=wasm32-wasi --sysroot=/foo --stdlib=libc++ %s 2>&1 \ ++// RUN: | FileCheck -check-prefix=EH_OFF_LINK %s ++// EH_OFF_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/noeh" "-L/foo/lib/wasm32-wasi" ++// ++// RUN: %clangxx -### --target=wasm32-wasi -fwasm-exceptions --sysroot=/foo --stdlib=libc++ %s 2>&1 \ ++// RUN: | FileCheck -check-prefix=EH_ON_LINK %s ++// EH_ON_LINK: wasm-ld{{.*}}" "-L/foo/lib/wasm32-wasi/eh" "-L/foo/lib/wasm32-wasi" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c573aea..9bb5fc2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,9 +67,9 @@ function(add_testcase runwasi test) # Apply language-specific options and dependencies. if(test MATCHES "cc$") - if(WASI_SDK_EXCEPTIONS) + if(NOT (WASI_SDK_EXCEPTIONS STREQUAL "OFF")) target_compile_options(${target_name} PRIVATE -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false) - target_link_options(${target_name} PRIVATE -lunwind) + target_link_options(${target_name} PRIVATE -fwasm-exceptions -lunwind) else() target_compile_options(${target_name} PRIVATE -fno-exceptions) endif() From 37ea0a6f25b6a7abe20d508667f58d552cdd297e Mon Sep 17 00:00:00 2001 From: Daniel Nicoletti Date: Fri, 10 Apr 2026 16:30:25 -0300 Subject: [PATCH 18/26] Add clang-scan-deps to the toolchain (#624) I tried to built it locally but failed, hopefully CI can do it. --- cmake/wasi-sdk-toolchain.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 7b05a98..1d023c8 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -56,6 +56,7 @@ set(tools clang-format clang-tidy clang-apply-replacements + clang-scan-deps lld llvm-addr2line llvm-mc From 600a442e16310214d16d14b15db59b1bbe8a8b9c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 14 Apr 2026 22:43:42 -0500 Subject: [PATCH 19/26] Update wasi-libc submodule (#625) Pulls in some final wasip3 bits here and there. --- src/wasi-libc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi-libc b/src/wasi-libc index 2fc32bc..ed9e99a 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit 2fc32bc81b9f07f8d9525edea59bfbaf760c06d6 +Subproject commit ed9e99a13c7c2d152b7f4347aaac42831362cdaf From dde480ab2811360f82405fbd91fc81fd4fd0a0e4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 20 Apr 2026 16:37:04 -0500 Subject: [PATCH 20/26] Refactor testing to avoid shell scripts, make matching more robust (#626) This commit is a refactor of how the test suite in this repository is specified. Previously an all-encompassing `testcase.sh` script would run all tests and assert various properties using various scripts and files that were present throughout the test suite. This has a number of downsides: * With CMake configuration it's not a fan of using `glob` to discover tests. * Using scripts is generally not that portable as commands can differ between platforms or be missing entirely (e.g. Windows). * The current "filter" scripts are pretty brittle and often need changes with newer Wasmtime versions. For example some tests don't pass with more recent Wasmtime versions due to output changes. Instead tests are now specified manually in `CMakeLists.txt` rather than globbed up, and various options are plumbed through to CTest in CMake. This makes the tests literally just a single invocation of `wasmtime` where CTest has various assertions about the result of the process execution after-the-fact. This gets all tests passing with the latest version of Wasmtime and additionally updates CI as well. --- .github/actions/install-deps/action.yml | 2 +- tests/CMakeLists.txt | 90 +++++++++++----- tests/compile-only/CMakeLists.txt | 12 +-- tests/exit_status_zero | 1 - tests/general/CMakeLists.txt | 75 +++++++++++-- tests/general/abort.c.exit_status.expected | 1 - tests/general/abort.c.stderr.expected | 7 -- tests/general/abort.c.stderr.expected.filter | 7 -- .../general/argc_argv_main.c.stdout.expected | 1 - .../general/argc_argv_main.cc.stdout.expected | 1 - .../assert-fail.c.exit_status.expected | 1 - tests/general/assert-fail.c.stderr.expected | 8 -- .../assert-fail.c.stderr.expected.filter | 8 -- tests/general/ctors_dtors.c.stdout.expected | 10 -- tests/general/ctors_dtors.cc.stdout.expected | 12 --- tests/general/env-absent.c.stdout.expected | 1 - tests/general/env.c.env | 1 - tests/general/env.c.stdout.expected | 1 - tests/general/environ.c.env | 1 - tests/general/environ.c.stdout.expected | 1 - .../general/iostream_main.cc.stdout.expected | 1 - tests/general/main_errno.c.stdout.expected | 1 - tests/general/mmap.c.stdout.expected | 3 - tests/general/no_arg_main.c.stdout.expected | 1 - tests/general/no_arg_main.cc.stdout.expected | 1 - ...intf-long-double-enabled.c.stdout.expected | 1 - .../general/printf-no-float.c.stdout.expected | 1 - .../printf-no-long-double.c.stdout.expected | 1 - tests/general/sigabrt.c.exit_status.expected | 1 - tests/general/sigabrt.c.stderr.expected | 6 -- .../general/sigabrt.c.stderr.expected.filter | 8 -- ...brt.c.wasm32-wasi-preview2.stderr.expected | 6 -- .../sigabrt.c.wasm32-wasip2.stderr.expected | 6 -- tests/general/signals.c.stderr.expected | 1 - tests/general/signals.c.stdout.expected | 4 - tests/general/void_main.c.stdout.expected | 1 - tests/general/void_main.cc.stdout.expected | 1 - tests/testcase.sh | 102 ------------------ 38 files changed, 133 insertions(+), 254 deletions(-) delete mode 100644 tests/exit_status_zero delete mode 100644 tests/general/abort.c.exit_status.expected delete mode 100644 tests/general/abort.c.stderr.expected delete mode 100755 tests/general/abort.c.stderr.expected.filter delete mode 100644 tests/general/argc_argv_main.c.stdout.expected delete mode 100644 tests/general/argc_argv_main.cc.stdout.expected delete mode 100644 tests/general/assert-fail.c.exit_status.expected delete mode 100644 tests/general/assert-fail.c.stderr.expected delete mode 100755 tests/general/assert-fail.c.stderr.expected.filter delete mode 100644 tests/general/ctors_dtors.c.stdout.expected delete mode 100644 tests/general/ctors_dtors.cc.stdout.expected delete mode 100644 tests/general/env-absent.c.stdout.expected delete mode 100644 tests/general/env.c.env delete mode 100644 tests/general/env.c.stdout.expected delete mode 100644 tests/general/environ.c.env delete mode 100644 tests/general/environ.c.stdout.expected delete mode 100644 tests/general/iostream_main.cc.stdout.expected delete mode 100644 tests/general/main_errno.c.stdout.expected delete mode 100644 tests/general/mmap.c.stdout.expected delete mode 100644 tests/general/no_arg_main.c.stdout.expected delete mode 100644 tests/general/no_arg_main.cc.stdout.expected delete mode 100644 tests/general/printf-long-double-enabled.c.stdout.expected delete mode 100644 tests/general/printf-no-float.c.stdout.expected delete mode 100644 tests/general/printf-no-long-double.c.stdout.expected delete mode 100644 tests/general/sigabrt.c.exit_status.expected delete mode 100644 tests/general/sigabrt.c.stderr.expected delete mode 100755 tests/general/sigabrt.c.stderr.expected.filter delete mode 100644 tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected delete mode 100644 tests/general/sigabrt.c.wasm32-wasip2.stderr.expected delete mode 100644 tests/general/signals.c.stderr.expected delete mode 100644 tests/general/signals.c.stdout.expected delete mode 100644 tests/general/void_main.c.stdout.expected delete mode 100644 tests/general/void_main.cc.stdout.expected delete mode 100755 tests/testcase.sh diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml index e3d00f8..da3cb0f 100644 --- a/.github/actions/install-deps/action.yml +++ b/.github/actions/install-deps/action.yml @@ -7,7 +7,7 @@ runs: - name: Setup `wasmtime` for tests uses: bytecodealliance/actions/wasmtime/setup@v1 with: - version: "41.0.3" + version: "44.0.0" - name: Install ccache, ninja (macOS) run: brew install ccache ninja if: runner.os == 'macOS' diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9bb5fc2..3b5972d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,12 +28,26 @@ set(opt_flags -O0 -O2 "-O2 -flto") add_custom_target(build-tests) -# Executes a single `test` specified. +# Registers `test` with CMake, compiling it with a number of flag combinations +# and for all enabled targets. This will register up to many tests with CTest. # -# This will compile `test` for all the various targets and with various -# compiler options. If `runwasi` is non-empty then the test will be executed -# in that runner as well. -function(add_testcase runwasi test) +# This function takes CMake-style arguments, specified as: +# +# * `EMULATED_CLOCKS` - enables `-lwasi-emulated-process-clocks` when compiling. +# * `EMULATED_MMAN` - enables `-lwasi-emulated-mman` when compiling. +# * `EMULATED_SIGNAL` - enables `-lwasi-emulated-signal` when compiling. +# * `PRINTSCAN_LONG_DOUBLE` - enables `-lc-printscan-long-double` when compiling. +# * `FSDIR` - requires `${test}.dir` to exist and mounts it when running the test +# * `PASS_REGULAR_EXPRESSION` - same as the CTest property +# * `ENV` - env vars (the `--env` flag in Wasmtime) to pass to the test. +# * `COMPILE_ONLY` - does not actually execute this test, just compiles it. +function(add_testcase test) + set(options EMULATED_CLOCKS EMULATED_MMAN EMULATED_SIGNAL + PRINTSCAN_LONG_DOUBLE FSDIR COMPILE_ONLY) + set(oneValueArgs PASS_REGULAR_EXPRESSION) + set(multiValueArgs ENV) + cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${oneValueArgs}" "${multiValueArgs}") + foreach(target IN LISTS WASI_SDK_TARGETS) foreach(compile_flags IN LISTS opt_flags) # Mangle the options into something appropriate for a CMake rule name @@ -52,16 +66,19 @@ function(add_testcase runwasi test) endif() # Apply test-specific compile options and link flags. - if(test MATCHES "clocks.c$") + if(${arg_EMULATED_CLOCKS}) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_PROCESS_CLOCKS) target_link_options(${target_name} PRIVATE -lwasi-emulated-process-clocks) - elseif(test MATCHES "mmap.c$") + endif() + if(${arg_EMULATED_MMAN}) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_MMAN) target_link_options(${target_name} PRIVATE -lwasi-emulated-mman) - elseif(test MATCHES "(sigabrt|signals).c$") + endif() + if(${arg_EMULATED_SIGNAL}) target_compile_options(${target_name} PRIVATE -D_WASI_EMULATED_SIGNAL) target_link_options(${target_name} PRIVATE -lwasi-emulated-signal) - elseif(test MATCHES "printf-long-double-enabled.c$") + endif() + if(${arg_PRINTSCAN_LONG_DOUBLE}) target_link_options(${target_name} PRIVATE -lc-printscan-long-double) endif() @@ -93,27 +110,42 @@ function(add_testcase runwasi test) target_link_options(${target_name} PRIVATE -Wno-deprecated) endif() - if(runwasi) - set(runner ${runwasi}) - if(${runner} MATCHES wasmtime) - if(target MATCHES threads) - set(runner "${runner} -Wshared-memory") - endif() - if(WASI_SDK_EXCEPTIONS) - set(runner "${runner} -Wexceptions") - endif() - if(target MATCHES "wasip3") - set(runner "${runner} -Wcomponent-model-async -Sp3") - endif() + if(arg_COMPILE_ONLY) + continue() + endif() + + set(runner ${WASI_SDK_RUNWASI}) + set(args) + if(${runner} MATCHES wasmtime) + if(target MATCHES threads) + list(APPEND runner -Wshared-memory) endif() - add_test( - NAME test-${target_name} - COMMAND - bash ../testcase.sh - ${runner} - ${test} - $ - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(WASI_SDK_EXCEPTIONS) + list(APPEND runner -Wexceptions) + endif() + if(target MATCHES "wasip3") + list(APPEND runner -Wcomponent-model-async -Sp3) + endif() + endif() + + foreach(env IN LISTS arg_ENV) + list(APPEND runner --env ${env}) + endforeach() + + if (${arg_FSDIR}) + list(APPEND runner --dir ${CMAKE_CURRENT_SOURCE_DIR}/${test}.dir::${test}.dir) + list(APPEND args ${test}.dir) + endif() + + add_test( + NAME test-${target_name} + COMMAND + ${runner} + $ + ${args} + ) + if (arg_PASS_REGULAR_EXPRESSION) + set_tests_properties(test-${target_name} PROPERTIES PASS_REGULAR_EXPRESSION ${arg_PASS_REGULAR_EXPRESSION}) endif() endforeach() endforeach() diff --git a/tests/compile-only/CMakeLists.txt b/tests/compile-only/CMakeLists.txt index 484a9cd..909b053 100644 --- a/tests/compile-only/CMakeLists.txt +++ b/tests/compile-only/CMakeLists.txt @@ -1,9 +1,3 @@ -file(GLOB c_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.c") -file(GLOB cxx_compile_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cc") - -set(compile_tests ${c_compile_tests} ${cxx_compile_tests}) - -foreach(test IN LISTS compile_tests) - add_testcase("" ${test}) -endforeach() - +add_testcase(addresses.c COMPILE_ONLY) +add_testcase(printf-long-double.c COMPILE_ONLY) +add_testcase(test.cc COMPILE_ONLY) diff --git a/tests/exit_status_zero b/tests/exit_status_zero deleted file mode 100644 index 573541a..0000000 --- a/tests/exit_status_zero +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/general/CMakeLists.txt b/tests/general/CMakeLists.txt index 2954fd1..189a500 100644 --- a/tests/general/CMakeLists.txt +++ b/tests/general/CMakeLists.txt @@ -1,8 +1,69 @@ -file(GLOB c_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.c") -file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS "*.cc") +add_testcase(abort.c PASS_REGULAR_EXPRESSION "wasm trap.*unreachable") +add_testcase(argc_argv_main.c PASS_REGULAR_EXPRESSION "hello from argc argv main") +add_testcase(argc_argv_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ argc argv main") +add_testcase(assert-fail.c PASS_REGULAR_EXPRESSION "Assertion failed: false.*wasm trap:.*unreachable") +add_testcase(assert-pass.c) +add_testcase(clocks.c EMULATED_CLOCKS) +add_testcase(cpp_thread_local.cc) +add_testcase(ctors_dtors.c PASS_REGULAR_EXPRESSION "\ +hello from_constructor101 +hello from_constructor +hello from_constructor65535 +hello main +goodbye main +hello another_from_atexit +hello from_atexit +hello from_destructor65535 +hello from_destructor +hello from_destructor101 +" +) +add_testcase(ctors_dtors.cc PASS_REGULAR_EXPRESSION "\ +hello from_constructor101 +hello from_constructor +hello from_constructor65535 +hello StaticObject::StaticObject +hello main +goodbye main +hello another_from_atexit +hello from_atexit +hello from_destructor65535 +hello from_destructor +hello StaticObject::~StaticObject +hello from_destructor101 +") +add_testcase(empty.c) +add_testcase(env-absent.c PASS_REGULAR_EXPRESSION "HELLO = \\(null\\)") +add_testcase(env.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello") +add_testcase(environ.c ENV HELLO=hello PASS_REGULAR_EXPRESSION "HELLO = hello") +add_testcase(exceptions.cc) +add_testcase(getentropy.c) +add_testcase(iostream_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ main with cout") +add_testcase(main_errno.c PASS_REGULAR_EXPRESSION "initial errno is 0: Success") +add_testcase(mmap.c EMULATED_MMAN FSDIR PASS_REGULAR_EXPRESSION "\ +“Would you tell me, please, which way I ought to go from here\\?” -set(general_tests ${c_general_tests} ${cxx_general_tests}) - -foreach(test IN LISTS general_tests) - add_testcase(${WASI_SDK_RUNWASI} ${test}) -endforeach() +“That depends a good deal on where you want to get to,” said the Cat. +" +) +add_testcase(no_arg_main.c PASS_REGULAR_EXPRESSION "hello from no-arg main") +add_testcase(no_arg_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ no-arg main") +add_testcase(opendir.c FSDIR) +add_testcase(printf-long-double-enabled.c PRINTSCAN_LONG_DOUBLE PASS_REGULAR_EXPRESSION "the answer is 42.000000") +add_testcase(printf-no-float.c PASS_REGULAR_EXPRESSION "the answer is 42") +add_testcase(printf-no-long-double.c PASS_REGULAR_EXPRESSION "the answer is 42.000000") +add_testcase(sigabrt.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\ +raising SIGABRT... +Program received fatal signal: Aborted +.*failed to run main module.* +") +add_testcase(signals.c EMULATED_SIGNAL PASS_REGULAR_EXPRESSION "\ +psignal message for SIGINT: Interrupt +strsignal for SIGHUP: 'Hangup' +beginning handler test: +handler for signal Window changed +finished handler test +") +add_testcase(stat.c FSDIR) +add_testcase(void_main.c PASS_REGULAR_EXPRESSION "hello from void main") +add_testcase(void_main.cc PASS_REGULAR_EXPRESSION "hello from C\\+\\+ void main") diff --git a/tests/general/abort.c.exit_status.expected b/tests/general/abort.c.exit_status.expected deleted file mode 100644 index 405e2af..0000000 --- a/tests/general/abort.c.exit_status.expected +++ /dev/null @@ -1 +0,0 @@ -134 diff --git a/tests/general/abort.c.stderr.expected b/tests/general/abort.c.stderr.expected deleted file mode 100644 index d533e58..0000000 --- a/tests/general/abort.c.stderr.expected +++ /dev/null @@ -1,7 +0,0 @@ -Error: failed to run main module `abort.c.---.wasm` - -Caused by: - 0: failed to invoke --- - 1: error while executing at wasm backtrace: - note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information - 2: wasm trap: wasm `unreachable` instruction executed diff --git a/tests/general/abort.c.stderr.expected.filter b/tests/general/abort.c.stderr.expected.filter deleted file mode 100755 index 98233a1..0000000 --- a/tests/general/abort.c.stderr.expected.filter +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cat \ - | sed -e 's/main module `.*abort\.c\.wasm`/main module `abort.c.---.wasm`/' \ - | sed -e 's/failed to invoke.*/failed to invoke ---/' \ - | sed -E '/0x[[:xdigit:]]+/d' diff --git a/tests/general/argc_argv_main.c.stdout.expected b/tests/general/argc_argv_main.c.stdout.expected deleted file mode 100644 index 851e349..0000000 --- a/tests/general/argc_argv_main.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from argc argv main! diff --git a/tests/general/argc_argv_main.cc.stdout.expected b/tests/general/argc_argv_main.cc.stdout.expected deleted file mode 100644 index 8d6f69a..0000000 --- a/tests/general/argc_argv_main.cc.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from C++ argc argv main! diff --git a/tests/general/assert-fail.c.exit_status.expected b/tests/general/assert-fail.c.exit_status.expected deleted file mode 100644 index 405e2af..0000000 --- a/tests/general/assert-fail.c.exit_status.expected +++ /dev/null @@ -1 +0,0 @@ -134 diff --git a/tests/general/assert-fail.c.stderr.expected b/tests/general/assert-fail.c.stderr.expected deleted file mode 100644 index fcf1f75..0000000 --- a/tests/general/assert-fail.c.stderr.expected +++ /dev/null @@ -1,8 +0,0 @@ -Assertion failed: false (assert-fail.c: main: 9) -Error: failed to run main module `assert-fail.c.---.wasm` - -Caused by: - 0: failed to invoke --- - 1: error while executing at wasm backtrace: - note: using the `WASMTIME_BACKTRACE_DETAILS=1` environment variable may show more debugging information - 2: wasm trap: wasm `unreachable` instruction executed diff --git a/tests/general/assert-fail.c.stderr.expected.filter b/tests/general/assert-fail.c.stderr.expected.filter deleted file mode 100755 index 77849a1..0000000 --- a/tests/general/assert-fail.c.stderr.expected.filter +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -cat \ - | sed -e 's/main module `.*assert-fail\.c\.wasm`/main module `assert-fail.c.---.wasm`/' \ - | sed -e 's/failed to invoke.*/failed to invoke ---/' \ - | sed -e 's/Assertion failed: false (.*assert-fail.c/Assertion failed: false (assert-fail.c/' \ - | sed -E '/0x[[:xdigit:]]+/d' diff --git a/tests/general/ctors_dtors.c.stdout.expected b/tests/general/ctors_dtors.c.stdout.expected deleted file mode 100644 index dfe8292..0000000 --- a/tests/general/ctors_dtors.c.stdout.expected +++ /dev/null @@ -1,10 +0,0 @@ -hello from_constructor101 -hello from_constructor -hello from_constructor65535 -hello main -goodbye main -hello another_from_atexit -hello from_atexit -hello from_destructor65535 -hello from_destructor -hello from_destructor101 diff --git a/tests/general/ctors_dtors.cc.stdout.expected b/tests/general/ctors_dtors.cc.stdout.expected deleted file mode 100644 index 675900b..0000000 --- a/tests/general/ctors_dtors.cc.stdout.expected +++ /dev/null @@ -1,12 +0,0 @@ -hello from_constructor101 -hello from_constructor -hello from_constructor65535 -hello StaticObject::StaticObject -hello main -goodbye main -hello another_from_atexit -hello from_atexit -hello from_destructor65535 -hello from_destructor -hello StaticObject::~StaticObject -hello from_destructor101 diff --git a/tests/general/env-absent.c.stdout.expected b/tests/general/env-absent.c.stdout.expected deleted file mode 100644 index f5614c3..0000000 --- a/tests/general/env-absent.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -HELLO = (null) diff --git a/tests/general/env.c.env b/tests/general/env.c.env deleted file mode 100644 index 9be21df..0000000 --- a/tests/general/env.c.env +++ /dev/null @@ -1 +0,0 @@ -HELLO=hello diff --git a/tests/general/env.c.stdout.expected b/tests/general/env.c.stdout.expected deleted file mode 100644 index b9a2a66..0000000 --- a/tests/general/env.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -HELLO = hello diff --git a/tests/general/environ.c.env b/tests/general/environ.c.env deleted file mode 100644 index 9be21df..0000000 --- a/tests/general/environ.c.env +++ /dev/null @@ -1 +0,0 @@ -HELLO=hello diff --git a/tests/general/environ.c.stdout.expected b/tests/general/environ.c.stdout.expected deleted file mode 100644 index b9a2a66..0000000 --- a/tests/general/environ.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -HELLO = hello diff --git a/tests/general/iostream_main.cc.stdout.expected b/tests/general/iostream_main.cc.stdout.expected deleted file mode 100644 index bbbdf77..0000000 --- a/tests/general/iostream_main.cc.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from C++ main with cout! diff --git a/tests/general/main_errno.c.stdout.expected b/tests/general/main_errno.c.stdout.expected deleted file mode 100644 index c537e5f..0000000 --- a/tests/general/main_errno.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -initial errno is 0: Success diff --git a/tests/general/mmap.c.stdout.expected b/tests/general/mmap.c.stdout.expected deleted file mode 100644 index 5011009..0000000 --- a/tests/general/mmap.c.stdout.expected +++ /dev/null @@ -1,3 +0,0 @@ -“Would you tell me, please, which way I ought to go from here?” - -“That depends a good deal on where you want to get to,” said the Cat. diff --git a/tests/general/no_arg_main.c.stdout.expected b/tests/general/no_arg_main.c.stdout.expected deleted file mode 100644 index d5ae29d..0000000 --- a/tests/general/no_arg_main.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from no-arg main! diff --git a/tests/general/no_arg_main.cc.stdout.expected b/tests/general/no_arg_main.cc.stdout.expected deleted file mode 100644 index 1aa719e..0000000 --- a/tests/general/no_arg_main.cc.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from C++ no-arg main! diff --git a/tests/general/printf-long-double-enabled.c.stdout.expected b/tests/general/printf-long-double-enabled.c.stdout.expected deleted file mode 100644 index e23e024..0000000 --- a/tests/general/printf-long-double-enabled.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -the answer is 42.000000 diff --git a/tests/general/printf-no-float.c.stdout.expected b/tests/general/printf-no-float.c.stdout.expected deleted file mode 100644 index 5bdef3d..0000000 --- a/tests/general/printf-no-float.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -the answer is 42 diff --git a/tests/general/printf-no-long-double.c.stdout.expected b/tests/general/printf-no-long-double.c.stdout.expected deleted file mode 100644 index e23e024..0000000 --- a/tests/general/printf-no-long-double.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -the answer is 42.000000 diff --git a/tests/general/sigabrt.c.exit_status.expected b/tests/general/sigabrt.c.exit_status.expected deleted file mode 100644 index 405e2af..0000000 --- a/tests/general/sigabrt.c.exit_status.expected +++ /dev/null @@ -1 +0,0 @@ -134 diff --git a/tests/general/sigabrt.c.stderr.expected b/tests/general/sigabrt.c.stderr.expected deleted file mode 100644 index d702a88..0000000 --- a/tests/general/sigabrt.c.stderr.expected +++ /dev/null @@ -1,6 +0,0 @@ -raising SIGABRT... -Program received fatal signal: Aborted -Error: failed to run main module `sigabrt.c.---.wasm` - -Caused by: - 0: failed to invoke --- diff --git a/tests/general/sigabrt.c.stderr.expected.filter b/tests/general/sigabrt.c.stderr.expected.filter deleted file mode 100755 index 726f024..0000000 --- a/tests/general/sigabrt.c.stderr.expected.filter +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -euo pipefail - -cat \ - | sed -e 's/main module `.*sigabrt\.c\.wasm`/main module `sigabrt.c.---.wasm`/' \ - | sed -e 's/source location: @[[:xdigit:]]*$/source location: @----/' \ - | sed -e 's/failed to invoke.*/failed to invoke ---/' \ - | head -n 6 diff --git a/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected b/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected deleted file mode 100644 index f437a82..0000000 --- a/tests/general/sigabrt.c.wasm32-wasi-preview2.stderr.expected +++ /dev/null @@ -1,6 +0,0 @@ -raising SIGABRT... -Program received fatal signal: Aborted -Error: failed to run main module `sigabrt.c.---.wasm` - -Caused by: - 0: failed to invoke `run` function diff --git a/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected b/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected deleted file mode 100644 index f437a82..0000000 --- a/tests/general/sigabrt.c.wasm32-wasip2.stderr.expected +++ /dev/null @@ -1,6 +0,0 @@ -raising SIGABRT... -Program received fatal signal: Aborted -Error: failed to run main module `sigabrt.c.---.wasm` - -Caused by: - 0: failed to invoke `run` function diff --git a/tests/general/signals.c.stderr.expected b/tests/general/signals.c.stderr.expected deleted file mode 100644 index f1b48ab..0000000 --- a/tests/general/signals.c.stderr.expected +++ /dev/null @@ -1 +0,0 @@ -psignal message for SIGINT: Interrupt diff --git a/tests/general/signals.c.stdout.expected b/tests/general/signals.c.stdout.expected deleted file mode 100644 index b35f256..0000000 --- a/tests/general/signals.c.stdout.expected +++ /dev/null @@ -1,4 +0,0 @@ -strsignal for SIGHUP: 'Hangup' -beginning handler test: -handler for signal Window changed -finished handler test diff --git a/tests/general/void_main.c.stdout.expected b/tests/general/void_main.c.stdout.expected deleted file mode 100644 index 533c359..0000000 --- a/tests/general/void_main.c.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from void main! diff --git a/tests/general/void_main.cc.stdout.expected b/tests/general/void_main.cc.stdout.expected deleted file mode 100644 index d1f259d..0000000 --- a/tests/general/void_main.cc.stdout.expected +++ /dev/null @@ -1 +0,0 @@ -hello from C++ void main! diff --git a/tests/testcase.sh b/tests/testcase.sh deleted file mode 100755 index d2c5a8c..0000000 --- a/tests/testcase.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -set -ueo pipefail - -# A simple testcase runner that runs a command, captures all its command-line -# outputs, and compares them against expected outputs. - -# Command-line parsing; this script is meant to be run from a higher-level -# script, so don't do anything fancy. -runwasi="$1" -input="$2" -wasm="$3" - -# Compile names for generated files. -stdout_observed="$wasm.stdout.observed" -stderr_observed="$wasm.stderr.observed" -exit_status_observed="$wasm.exit_status.observed" - -# Double-check that a runwasi command was specified since otherwise this script -# was invoked with no arguments which isn't as intended. -if [ "$runwasi" == "" ]; then - exit 1 -fi - -# Determine the input file to write to stdin. -if [ -e "$input.stdin" ]; then - stdin="$input.stdin" -else - stdin="/dev/null" -fi - -# Determine any environment variables to set. -if [ -e "$input.env" ]; then - env=$(sed -e 's/^/--env /' < "$input.env") -else - env="" -fi - -# Determine a preopened directory to provide. -if [ -e "$input.dir" ]; then - dir="--dir $input.dir" - dirarg="$input.dir" -else - dir="" - dirarg="" -fi - -# Run the test, capturing stdout, stderr, and the exit status. -exit_status=0 -$runwasi $env $dir "$wasm" $dirarg \ - < "$stdin" \ - > "$stdout_observed" \ - 2> "$stderr_observed" \ - || exit_status=$? -echo $exit_status > "$exit_status_observed" - -# On Windows Wasmtime will exit with error code 3 for aborts. On Unix Wasmtime -# will exit with status 134. Paper over this difference by pretending to be Unix -# on Windows and converting exit code 3 into 134 for the purposes of asserting -# test output. -if [ "$OSTYPE" = "msys" ] && [ "$exit_status" = "3" ]; then - echo 134 > "$exit_status_observed" -fi - -# Determine the reference files to compare with. -if [ -e "$input.stdout.expected" ]; then - stdout_expected="$input.stdout.expected" - - # Apply output filters. - if [ -e "$input.stdout.expected.filter" ]; then - cat "$stdout_observed" \ - | "$input.stdout.expected.filter" \ - > "${stdout_observed}.filtered" - stdout_observed="${stdout_observed}.filtered" - fi -else - stdout_expected="/dev/null" -fi - -if [ -e "$input.stderr.expected" ]; then - stderr_expected="$input.stderr.expected" - - # Apply output filters. - if [ -e "$input.stderr.expected.filter" ]; then - cat "$stderr_observed" \ - | "./$input.stderr.expected.filter" \ - > "${stderr_observed}.filtered" - stderr_observed="${stderr_observed}.filtered" - fi -else - stderr_expected="/dev/null" -fi -if [ -e "$input.exit_status.expected" ]; then - exit_status_expected="$input.exit_status.expected" -else - exit_status_expected=../exit_status_zero -fi - -# If there are any differences, diff will return a non-zero exit status, and -# since this script uses "set -e", it will return a non-zero exit status too. -diff --ignore-space-change -u "$stderr_expected" "$stderr_observed" -diff --ignore-space-change -u "$stdout_expected" "$stdout_observed" -diff --ignore-space-change -u "$exit_status_expected" "$exit_status_observed" From c10c0507deb3e5aad506f1f9f32084e49a21834b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 28 Apr 2026 15:52:41 -0500 Subject: [PATCH 21/26] Update wasi-libc (#627) Pull in recent updates --- src/wasi-libc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasi-libc b/src/wasi-libc index ed9e99a..161b319 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit ed9e99a13c7c2d152b7f4347aaac42831362cdaf +Subproject commit 161b3195fc2558d2b1ba3eb9ffae3b2b47407623 From 0f865f3fe988f12117f6c700c3520e2bbe57aeac Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 4 May 2026 23:24:46 +0900 Subject: [PATCH 22/26] reduce whitespace differences among cmake toolchain files (#631) no functional changes are intended. --- wasi-sdk-p1.cmake | 1 - wasi-sdk-p2.cmake | 2 +- wasi-sdk-p3.cmake | 2 +- wasi-sdk-pthread.cmake | 2 +- wasi-sdk.cmake | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/wasi-sdk-p1.cmake b/wasi-sdk-p1.cmake index 9038301..a642480 100644 --- a/wasi-sdk-p1.cmake +++ b/wasi-sdk-p1.cmake @@ -31,7 +31,6 @@ set(CMAKE_ASM_COMPILER_TARGET ${triple}) # Don't look in the sysroot for executables to run during the build set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - # Only look in the sysroot (not in the host paths) for the rest set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/wasi-sdk-p2.cmake b/wasi-sdk-p2.cmake index aeed293..19604d7 100644 --- a/wasi-sdk-p2.cmake +++ b/wasi-sdk-p2.cmake @@ -17,7 +17,7 @@ endif() # When building from source, WASI_SDK_PREFIX represents the generated directory if(NOT WASI_SDK_PREFIX) - set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) endif() set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) diff --git a/wasi-sdk-p3.cmake b/wasi-sdk-p3.cmake index f737562..d4f01cf 100644 --- a/wasi-sdk-p3.cmake +++ b/wasi-sdk-p3.cmake @@ -17,7 +17,7 @@ endif() # When building from source, WASI_SDK_PREFIX represents the generated directory if(NOT WASI_SDK_PREFIX) - set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) endif() set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) diff --git a/wasi-sdk-pthread.cmake b/wasi-sdk-pthread.cmake index c2cd83d..98e5209 100644 --- a/wasi-sdk-pthread.cmake +++ b/wasi-sdk-pthread.cmake @@ -20,7 +20,7 @@ endif() # When building from source, WASI_SDK_PREFIX represents the generated directory if(NOT WASI_SDK_PREFIX) - set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) endif() set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) diff --git a/wasi-sdk.cmake b/wasi-sdk.cmake index 2131cfc..7c9c711 100644 --- a/wasi-sdk.cmake +++ b/wasi-sdk.cmake @@ -17,7 +17,7 @@ endif() # When building from source, WASI_SDK_PREFIX represents the generated directory if(NOT WASI_SDK_PREFIX) - set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) + set(WASI_SDK_PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../) endif() set(CMAKE_C_COMPILER ${WASI_SDK_PREFIX}/bin/clang${WASI_HOST_EXE_SUFFIX}) From 3a57aa06289ee679a62119d8842ca9ee7a4e5ee9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2026 12:20:07 -0500 Subject: [PATCH 23/26] Update `CppExceptions.md` documentation (#630) * Document known incompatibility with LTO * Update now that wasi-sdk-33 is released --- CppExceptions.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CppExceptions.md b/CppExceptions.md index 1ca79bf..0e8bbce 100644 --- a/CppExceptions.md +++ b/CppExceptions.md @@ -1,8 +1,5 @@ # Support for C++ Exceptions -> **Note**: this documentation does not cover wasi-sdk-31, the latest version -> of wasi-sdk at this time. - From wasi-sdk-33 and onwards the artifacts produced by this repository support compiling C++ code both with and without exceptions. The sysroot for wasm targets contains two copies of the C++ standard library and headers -- one with @@ -68,3 +65,7 @@ wasi-sdk at this time: all built with the standard exception-handling proposal. * Currently `-lunwind` is required when linking, but this may become automatic in the future. +* Currently exceptions and LTO have a known issue and may not work. See + [WebAssembly/wasi-sdk#629][#629] for more information. + +[#629]: https://github.com/WebAssembly/wasi-sdk/issues/629 From d05f757dc23dec073f8cbc6ba381e3b6b320b61c Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 15 May 2026 22:28:13 -0400 Subject: [PATCH 24/26] Add WASI_SDK_BUILD_SHARED option (#632) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new CMake option, defaulting to `ON` so existing builds are unaffected. When set to `OFF`, `wasi-libc` is built with `-DBUILD_SHARED=OFF` (its existing `if (NOT BUILD_SHARED)` block then short-circuits the per-target `set(SHARED ON)` for `wasm32-wasi`/`wasm32-wasip1`), and the `libcxx`/`libcxxabi`/`libunwind` invocations no longer use `${pic}` directly for their `*_ENABLE_SHARED` flags — they go through a new local `shared` variable that AND's `WASI_SDK_BUILD_SHARED` with `${pic}`. Motivation: downstream consumers that build wasi-sdk from source as part of a larger toolchain pipeline (in our case, Firefox's build system) don't ship or link against shared sysroot artifacts. The current code path provides no way to opt out, so we've been patching locally. The existing comment in `wasi-sdk-sysroot.cmake` notes shared libraries aren't supported with LTO; this doesn't change that default, it just exposes the same toggle for non-LTO builds too. A `static` entry is added to the `build-only-sysroot` CI matrix to exercise the OFF path. --- .github/workflows/main.yml | 2 ++ README.md | 3 +++ cmake/wasi-sdk-sysroot.cmake | 25 ++++++++++++++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2644c8f..a891a60 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -181,6 +181,8 @@ jobs: - name: default - name: exceptions defines: -DWASI_SDK_EXCEPTIONS=ON + - name: static + defines: -DWASI_SDK_BUILD_SHARED=OFF steps: - uses: actions/checkout@v6 with: diff --git a/README.md b/README.md index 7893c96..43022e8 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,9 @@ in compiling WebAssembly code. Supported CMake flags are: to enable/disable. The default is `-mcpu=lime1`. * `-DWASI_SDK_LTO=ON` - whether to enable/disable builds of LTO-capable libraries as part of the build. +* `-DWASI_SDK_BUILD_SHARED=ON` - whether to build shared libraries (`libc.so`, + `libc++.so`, etc.) for targets that support them. Set to `OFF` to produce a + static-only sysroot. * `-DWASI_SDK_EXCEPTIONS=ON` - whether to enable/disable support for C++ exceptions, see [CppExceptions.md](./CppExceptions.md) for more information. * `-DWASI_SDK_TEST_HOST_TOOLCHAIN=ON` - test the host toolchain's wasi-libc and diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 491180e..613aaa7 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -29,6 +29,7 @@ option(WASI_SDK_DEBUG_PREFIX_MAP "Pass `-fdebug-prefix-map` for built artifacts" option(WASI_SDK_INCLUDE_TESTS "Whether or not to build tests by default" OFF) option(WASI_SDK_INSTALL_TO_CLANG_RESOURCE_DIR "Whether or not to modify the compiler's resource directory" OFF) option(WASI_SDK_LTO "Whether or not to build LTO assets" ON) +option(WASI_SDK_BUILD_SHARED "Whether or not to build shared libraries when supported" ON) set(WASI_SDK_EXCEPTIONS "${EXCEPTIONS_DEFAULT}" CACHE STRING "Whether or not C++ exceptions are enabled") set(WASI_SDK_CPU_CFLAGS "-mcpu=lime1" CACHE STRING "CFLAGS to specify wasm features to enable") @@ -192,9 +193,14 @@ function(define_wasi_libc_sub target target_suffix lto) set(extra_cmake_args) # Configure LTO in wasi libc if it's enabled. Be sure to disable shared - # libraries as well since that's not currently supported. + # libraries as well since that's not currently supported with LTO. if (lto) - list(APPEND extra_cmake_args -DLTO=full -DBUILD_SHARED=OFF) + list(APPEND extra_cmake_args -DLTO=full) + endif() + # WASI_SDK_BUILD_SHARED=OFF allows producing a static-only sysroot; the LTO + # branch above already implies shared-off, so the two conditions are combined. + if (lto OR NOT WASI_SDK_BUILD_SHARED) + list(APPEND extra_cmake_args -DBUILD_SHARED=OFF) endif() ExternalProject_Add(wasi-libc-${target}${target_suffix}-build @@ -292,6 +298,15 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ list(APPEND extra_flags -Wno-deprecated) endif() + # `shared` is computed here, after the exceptions branch above may have forced + # pic OFF, so that LIBCXX_ENABLE_SHARED/LIBCXXABI_ENABLE_SHARED/LIBUNWIND_ENABLE_SHARED + # stay consistent with the final value of CMAKE_POSITION_INDEPENDENT_CODE. + if(WASI_SDK_BUILD_SHARED AND pic) + set(shared ON) + else() + set(shared OFF) + endif() + set(extra_cflags_list ${CMAKE_C_FLAGS} ${extra_flags}) list(JOIN extra_cflags_list " " extra_cflags) set(extra_cxxflags_list ${CMAKE_CXX_FLAGS} ${extra_flags}) @@ -311,7 +326,7 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DLIBCXX_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXX_HAS_WIN32_THREAD_API:BOOL=OFF -DLLVM_COMPILER_CHECKED=ON - -DLIBCXX_ENABLE_SHARED:BOOL=${pic} + -DLIBCXX_ENABLE_SHARED:BOOL=${shared} -DLIBCXX_ENABLE_EXCEPTIONS:BOOL=${exceptions} -DLIBCXX_ENABLE_FILESYSTEM:BOOL=ON -DLIBCXX_ENABLE_ABI_LINKER_SCRIPT:BOOL=OFF @@ -319,14 +334,14 @@ function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_ -DLIBCXX_HAS_MUSL_LIBC:BOOL=OFF -DLIBCXX_ABI_VERSION=2 -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions} - -DLIBCXXABI_ENABLE_SHARED:BOOL=${pic} + -DLIBCXXABI_ENABLE_SHARED:BOOL=${shared} -DLIBCXXABI_SILENT_TERMINATE:BOOL=ON -DLIBCXXABI_ENABLE_THREADS:BOOL=ON -DLIBCXXABI_HAS_PTHREAD_API:BOOL=ON -DLIBCXXABI_HAS_EXTERNAL_THREAD_API:BOOL=OFF -DLIBCXXABI_HAS_WIN32_THREAD_API:BOOL=OFF -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions} - -DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} + -DLIBUNWIND_ENABLE_SHARED:BOOL=${shared} -DLIBUNWIND_ENABLE_THREADS:BOOL=ON -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON -DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF From 5faf80805397ae2a96ab224d1f103798af06dd92 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 8 Jun 2026 16:30:00 -0500 Subject: [PATCH 25/26] Set `CMAKE_EXECUTABLE_SUFFIX` in toolchain files (#634) Closes #633 --- wasi-sdk-p1.cmake | 1 + wasi-sdk-p2.cmake | 1 + wasi-sdk-p3.cmake | 1 + wasi-sdk-pthread.cmake | 1 + wasi-sdk.cmake | 1 + 5 files changed, 5 insertions(+) diff --git a/wasi-sdk-p1.cmake b/wasi-sdk-p1.cmake index a642480..d44077b 100644 --- a/wasi-sdk-p1.cmake +++ b/wasi-sdk-p1.cmake @@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(CMAKE_EXECUTABLE_SUFFIX .wasm) set(triple wasm32-wasip1) if(WIN32) diff --git a/wasi-sdk-p2.cmake b/wasi-sdk-p2.cmake index 19604d7..c69e63b 100644 --- a/wasi-sdk-p2.cmake +++ b/wasi-sdk-p2.cmake @@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(CMAKE_EXECUTABLE_SUFFIX .wasm) set(triple wasm32-wasip2) if(WIN32) diff --git a/wasi-sdk-p3.cmake b/wasi-sdk-p3.cmake index d4f01cf..9df62d4 100644 --- a/wasi-sdk-p3.cmake +++ b/wasi-sdk-p3.cmake @@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(CMAKE_EXECUTABLE_SUFFIX .wasm) set(triple wasm32-wasip3) if(WIN32) diff --git a/wasi-sdk-pthread.cmake b/wasi-sdk-pthread.cmake index 98e5209..219c033 100644 --- a/wasi-sdk-pthread.cmake +++ b/wasi-sdk-pthread.cmake @@ -11,6 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") # (--export-memory is implicit unless --import-memory is given) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--import-memory") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-memory") +set(CMAKE_EXECUTABLE_SUFFIX .wasm) if(WIN32) set(WASI_HOST_EXE_SUFFIX ".exe") diff --git a/wasi-sdk.cmake b/wasi-sdk.cmake index 7c9c711..2280f3b 100644 --- a/wasi-sdk.cmake +++ b/wasi-sdk.cmake @@ -7,6 +7,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") set(CMAKE_SYSTEM_NAME WASI) set(CMAKE_SYSTEM_VERSION 1) set(CMAKE_SYSTEM_PROCESSOR wasm32) +set(CMAKE_EXECUTABLE_SUFFIX .wasm) set(triple wasm32-wasi) if(WIN32) From 3b3e357ad9d2a082201f08da7f22bce87260133b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Jun 2026 20:50:02 -0500 Subject: [PATCH 26/26] Update wasm-component-ld (#635) Keeping it up-to-date --- cmake/wasi-sdk-toolchain.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/wasi-sdk-toolchain.cmake b/cmake/wasi-sdk-toolchain.cmake index 1d023c8..88d71e2 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -265,7 +265,7 @@ install(DIRECTORY ${wasi_tmp_install}/bin ${wasi_tmp_install}/lib ${wasi_tmp_ins # Build logic for `wasm-component-ld` installed from Rust code. set(wasm_component_ld_root ${CMAKE_CURRENT_BINARY_DIR}/wasm-component-ld) set(wasm_component_ld ${wasm_component_ld_root}/bin/wasm-component-ld${CMAKE_EXECUTABLE_SUFFIX}) -set(wasm_component_ld_version 0.5.22) +set(wasm_component_ld_version 0.5.24) if(RUST_TARGET) set(rust_target_flag --target=${RUST_TARGET}) endif()