diff --git a/.github/actions/install-deps/action.yml b/.github/actions/install-deps/action.yml index 80e3bb6..74dea0e 100644 --- a/.github/actions/install-deps/action.yml +++ b/.github/actions/install-deps/action.yml @@ -7,7 +7,11 @@ runs: - name: Setup `wasmtime` for tests uses: bytecodealliance/actions/wasmtime/setup@v1 with: +<<<<<<< HEAD version: "dev" +======= + version: "44.0.0" +>>>>>>> main - name: Install ccache, ninja (macOS) run: brew install ccache ninja if: runner.os == 'macOS' diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 14f7460..a891a60 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 @@ -53,7 +68,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 +80,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 @@ -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 @@ -125,7 +143,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,11 +167,48 @@ 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 }} + build-only-sysroot: + name: Build only sysroot - ${{ matrix.name }} + runs-on: ubuntu-24.04 + strategy: + matrix: + include: + - name: default + - name: exceptions + defines: -DWASI_SDK_EXCEPTIONS=ON + - name: static + defines: -DWASI_SDK_BUILD_SHARED=OFF + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: ./.github/actions/checkout + - uses: ./.github/actions/install-deps + - run: cargo install wasm-component-ld@0.5.21 + - 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-22/bin/clang \ + -DCMAKE_SYSTEM_NAME=WASI \ + -DWASI_SDK_INCLUDE_TESTS=ON \ + -DWASI_SDK_CPU_CFLAGS="" \ + -DCMAKE_C_LINKER_DEPFILE_SUPPORTED=OFF \ + -DCMAKE_CXX_LINKER_DEPFILE_SUPPORTED=OFF \ + ${{ matrix.defines }} + - run: ninja -C build + - run: ctest --output-on-failure --parallel 10 --test-dir build/tests + # Once all of the above matrix entries have completed this job will run and # assemble the final `wasi-sdk-*` artifacts by fusing the toolchain/sysroot # artifacts. @@ -162,16 +217,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 @@ -222,12 +277,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 diff --git a/.gitignore b/.gitignore index 9d0b71a..6ef0b60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build dist +experimental diff --git a/CppExceptions.md b/CppExceptions.md index 7ea1e2d..0e8bbce 100644 --- a/CppExceptions.md +++ b/CppExceptions.md @@ -1,25 +1,14 @@ # 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. +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. -## Building wasi-sdk with exceptions - -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 +25,47 @@ 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 + +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 +``` -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. +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 -## Future Plans +There are a few known limitations/bugs/todos around exceptions support in +wasi-sdk at this time: -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: +* 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. +* Currently exceptions and LTO have a known issue and may not work. See + [WebAssembly/wasi-sdk#629][#629] for more information. -* 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. +[#629]: https://github.com/WebAssembly/wasi-sdk/issues/629 diff --git a/README.md b/README.md index 97bb4f5..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 @@ -209,41 +212,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 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 + 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 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/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 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/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 90580b5..452b19b 100755 --- a/ci/merge-artifacts.sh +++ b/ci/merge-artifacts.sh @@ -34,8 +34,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 diff --git a/cmake/wasi-sdk-sysroot.cmake b/cmake/wasi-sdk-sysroot.cmake index 1b28417..da560e6 100644 --- a/cmake/wasi-sdk-sysroot.cmake +++ b/cmake/wasi-sdk-sysroot.cmake @@ -20,14 +20,34 @@ 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) +<<<<<<< HEAD option(WASI_SDK_EXCEPTIONS "Whether or not C++ exceptions are enabled" OFF) option(WASI_SDK_COOP_THREADING "Whether or not to build with cooperative threading support" OFF) +======= +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") +>>>>>>> main 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) @@ -102,11 +122,27 @@ function(define_compiler_rt target) add_dependencies(compiler-rt-build compiler-rt-build-${target}) endfunction() +<<<<<<< HEAD define_compiler_rt(wasm32-wasip3) +======= +# 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) +>>>>>>> main + +# 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 @@ -124,7 +160,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 # ============================================================================= @@ -134,8 +169,13 @@ 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}") +<<<<<<< HEAD if(${target} MATCHES p2 OR ${target} MATCHES p3) # Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets. +======= + if(${target} MATCHES "p[23]") + # Always enable `-fPIC` for the `wasm32-wasip2` and `wasm32-wasip3` targets. +>>>>>>> main # This makes `libc.a` more flexible and usable in dynamic linking situations. list(APPEND extra_cflags_list -fPIC) endif() @@ -157,9 +197,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 @@ -207,8 +252,13 @@ execute_process( OUTPUT_VARIABLE llvm_version OUTPUT_STRIP_TRAILING_WHITESPACE) +<<<<<<< HEAD function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix) if(${target} MATCHES threads OR ${target} MATCHES p3) +======= +function(define_libcxx_sub target target_suffix extra_target_flags extra_libdir_suffix exceptions) + if(${target} MATCHES threads) +>>>>>>> main set(pic OFF) set(target_flags -pthread) else() @@ -233,7 +283,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 @@ -242,6 +294,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 @@ -250,6 +309,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}) @@ -261,7 +329,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 @@ -269,22 +337,22 @@ 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_EXCEPTIONS:BOOL=${WASI_SDK_EXCEPTIONS} + -DLIBCXX_ENABLE_SHARED:BOOL=${shared} + -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_SHARED:BOOL=${pic} + -DLIBCXXABI_ENABLE_EXCEPTIONS:BOOL=${exceptions} + -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=${WASI_SDK_EXCEPTIONS} - -DLIBUNWIND_ENABLE_SHARED:BOOL=${pic} + -DLIBCXXABI_USE_LLVM_UNWINDER:BOOL=${exceptions} + -DLIBUNWIND_ENABLE_SHARED:BOOL=${shared} -DLIBUNWIND_ENABLE_THREADS:BOOL=ON -DLIBUNWIND_USE_COMPILER_RT:BOOL=ON -DLIBUNWIND_INCLUDE_TESTS:BOOL=OFF @@ -292,9 +360,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 @@ -309,18 +377,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 @@ -328,8 +423,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 e53d4c4..78a8bf6 100644 --- a/cmake/wasi-sdk-toolchain.cmake +++ b/cmake/wasi-sdk-toolchain.cmake @@ -5,6 +5,22 @@ 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}) +option(WASI_SDK_LIBXML2 "Whether or not to build libxml2 for LLDB" ON) + string(REGEX REPLACE "[ ]+" ";" llvm_cmake_flags_list "${LLVM_CMAKE_FLAGS}") set(wasi_tmp_install ${CMAKE_CURRENT_BINARY_DIR}/install) @@ -40,6 +56,7 @@ set(tools clang-format clang-tidy clang-apply-replacements + clang-scan-deps lld llvm-addr2line llvm-mc @@ -57,7 +74,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 @@ -72,6 +90,127 @@ 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_FBSDVMCORE=OFF + -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) + 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 + + # 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 + --disable-static + --disable-silent-rules + CC=${CMAKE_C_COMPILER} + LDFLAGS=${libedit_ldflags} + ${extra_configure_commands} + BUILD_COMMAND + ${MAKE_EXECUTABLE} -j${nproc} + + 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() + + 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() + + # 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 + 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) list(TRANSFORM tools PREPEND --target=install- OUTPUT_VARIABLE install_targets) @@ -87,12 +226,15 @@ 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 # 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 @@ -106,9 +248,13 @@ 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) +ExternalProject_Add_StepDependencies(llvm-build configure libedit libxml2) # Installation target for this outer project for installing the toolchain to the # system. @@ -119,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.21) +set(wasm_component_ld_version 0.5.24) if(RUST_TARGET) set(rust_target_flag --target=${RUST_TARGET}) endif() 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/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/src/wasi-libc b/src/wasi-libc index fd5840e..161b319 160000 --- a/src/wasi-libc +++ b/src/wasi-libc @@ -1 +1 @@ -Subproject commit fd5840e1ba9d23cbf610abe99f5ff59a4f1bec0e +Subproject commit 161b3195fc2558d2b1ba3eb9ffae3b2b47407623 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 852983b..0e1c263 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,24 +66,27 @@ 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() # 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() @@ -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 p3) - set(runner "${runner} -Wcomponent-model-async -Wcomponent-model-threading -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 ef0a552..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} "*.c") -file(GLOB cxx_general_tests RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.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 b/tests/general/abort.c index ddac207..658d386 100644 --- a/tests/general/abort.c +++ b/tests/general/abort.c @@ -1,6 +1,7 @@ #include - +void __wasm_set_tls_base(void *base); int main(void) { + __wasm_set_tls_base(NULL); abort(); return 0; } 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.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.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/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; } 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.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" diff --git a/wasi-sdk-p1.cmake b/wasi-sdk-p1.cmake index 9038301..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) @@ -31,7 +32,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..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) @@ -17,7 +18,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..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) @@ -17,7 +18,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..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") @@ -20,7 +21,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..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) @@ -17,7 +18,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})