Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,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: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
v=22
rel=$(lsb_release -cs)
sudo apt-add-repository "deb http://apt.llvm.org/$rel/ llvm-toolchain-$rel-$v main"
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="" \
Expand Down
77 changes: 43 additions & 34 deletions CppExceptions.md
Original file line number Diff line number Diff line change
@@ -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-32 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

Expand All @@ -36,25 +28,42 @@ 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.
* Currently `-fwasm-exceptions` is a required flag to enable C++ exceptions.
It's unclear whethe `-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-handlign proposal.
* Currently `-lunwind` is required when linking, but this may become automatic
in the future.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions ci/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
73 changes: 57 additions & 16 deletions cmake/wasi-sdk-sysroot.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand All @@ -327,27 +351,44 @@ 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-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})

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
# unless this one also exists:
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 $<$<BOOL:${WASI_SDK_LTO}>:libcxx-${target}-lto-build> libcxx-${target}-extra-dir)
add_dependencies(libcxx-${target} libcxx-${target}-extra-dir)
endfunction()

foreach(target IN LISTS WASI_SDK_TARGETS)
Expand Down
3 changes: 3 additions & 0 deletions cmake/wasi-sdk-toolchain.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,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)
Expand Down
27 changes: 11 additions & 16 deletions src/llvm-pr-168449.patch
Original file line number Diff line number Diff line change
@@ -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 <yerzhan@novel.systems>
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
Expand Down
Loading
Loading