From 29924ffd3b67bd6f04d5135adbeb5286aa5b6945 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 16:03:53 -0500 Subject: [PATCH 01/22] [wasi] Stand up CoreCLR-WASI library-test smoke leg Adds the infrastructure to run CoreCLR-WASI library tests on wasmtime via Helix, mirroring the browser-CoreCLR LibraryTestsCoreCLR legs. Follow-up to CoreCLR-WASI runtime tests (#130051); addresses #130129. Unlike Mono - which relinks dotnet.wasm per-app with wasi-sdk - CoreCLR-WASI ships a prebuilt corerun host in the runtime pack and loads managed assemblies from the wasmtime filesystem, so there is no per-app native relink. - WasiApp.InTree.props: RuntimeFlavor CoreCLR/Mono split (UseMonoRuntime); default invariant globalization for the CoreCLR bring-up. - WasiApp.InTree.targets: import WasiApp.CoreCLR.targets for RuntimeFlavor=CoreCLR. - WasiApp.CoreCLR.targets (new): reuse the shared WASI bundling with the native relink disabled; copy the prebuilt corerun and System.Private.CoreLib.dll (which ship in the pack's native/ dir) into managed/ so corerun finds the runtime via CORE_ROOT; skip the Mono-only _ReadWasmProps (wasm-props.json is not in the CoreCLR pack). - tests.wasi.targets: CoreCLR run command - managed/corerun WasmTestRunner.dll managed/.dll with -W exceptions=y and CORE_ROOT=/managed. - sendtohelix-wasi.targets: CoreCLR work-item prefix; gate Mono-only build-tooling helix payloads off for CoreCLR. - tests.proj: wasi CoreCLR smoke set (System.Runtime.Tests); gate the existing wasi smoke set to Mono. - New CI leg wasi-wasm-coreclr-library-tests.yml wired into runtime.yml (smoke-only, rolling alwaysRun, non-gating). The bundle build and run command were validated locally against a from-main wasi-wasm CoreCLR build: CoreCLR initializes and executes the managed entry point under wasmtime. Full green runs still depend on runtime-side fixes (interpreter callhelpers, corerun entry-assembly resolution). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../wasi-wasm-coreclr-library-tests.yml | 77 +++++++++++++++++++ eng/pipelines/runtime.yml | 10 +++ eng/testing/tests.wasi.targets | 20 ++++- src/libraries/sendtohelix-wasi.targets | 11 +++ src/libraries/tests.proj | 10 ++- src/mono/wasi/build/WasiApp.CoreCLR.targets | 73 ++++++++++++++++++ src/mono/wasi/build/WasiApp.InTree.props | 17 +++- src/mono/wasi/build/WasiApp.InTree.targets | 6 +- 8 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml create mode 100644 src/mono/wasi/build/WasiApp.CoreCLR.targets diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml new file mode 100644 index 00000000000000..48f1282394126c --- /dev/null +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -0,0 +1,77 @@ +parameters: + alwaysRun: false + extraBuildArgs: '' + extraHelixArguments: '' + isExtraPlatformsBuild: false + isWasmOnlyBuild: false + nameSuffix: '' + platforms: [] + scenarios: ['WasmTestOnWasmtime'] + shouldContinueOnError: false + shouldRunSmokeOnly: false + +jobs: + +# +# Build CoreCLR libraries for WASI and run the library tests on wasmtime via Helix. +# Mirrors wasm-coreclr-library-tests.yml (the browser-CoreCLR LibraryTestsCoreCLR leg), +# with the WasmTestOnWasmtime scenario and the wasi_wasm helix queue. +# See https://github.com/dotnet/runtime/issues/130129. +# +- template: /eng/pipelines/common/platform-matrix.yml + parameters: + jobTemplate: /eng/pipelines/common/global-build-job.yml + helixQueuesTemplate: /eng/pipelines/libraries/helix-queues-setup.yml + buildConfig: Release + runtimeFlavor: coreclr + platforms: ${{ parameters.platforms }} + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + variables: + - name: alwaysRunVar + value: ${{ parameters.alwaysRun }} + - name: shouldRunOnDefaultPipelines + value: $[ + or( + eq(variables['wasmDarcDependenciesChanged'], true), + eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true), + eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true), + eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_wasm_coreclr_runtimetests.containsChange'], true)) + ] + # run smoke tests only if: + # - explicitly requested + # - libraries or illink changed and no wasm specific changes + - name: shouldRunSmokeOnlyVar + value: $[ + or( + eq('${{ parameters.shouldRunSmokeOnly }}', 'true'), + and( + eq('${{ parameters.shouldRunSmokeOnly }}', 'onLibrariesAndIllinkChanges'), + ne(variables['wasmDarcDependenciesChanged'], true), + or( + eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_tools_illink.containsChange'], true), + eq(stageDependencies.EvaluatePaths.evaluate_paths.outputs['SetPathVars_libraries.containsChange'], true) + ) + ) + ) + ] + - name: _wasmRunSmokeTestsOnlyArg + value: /p:RunSmokeTestsOnly=$(shouldRunSmokeOnlyVar) + + jobParameters: + isExtraPlatforms: ${{ parameters.isExtraPlatformsBuild }} + testGroup: innerloop + nameSuffix: LibraryTestsCoreCLR_WASI${{ parameters.nameSuffix }} + buildArgs: -s clr+libs+host+packs+libs.tests -c $(_BuildConfig) /p:ArchiveTests=true $(_wasmRunSmokeTestsOnlyArg) /maxcpucount:1 ${{ parameters.extraBuildArgs }} + timeoutInMinutes: 240 + condition: >- + or( + eq(variables['alwaysRunVar'], true), + eq(variables['isDefaultPipeline'], variables['shouldRunOnDefaultPipelines'])) + # extra steps, run tests + postBuildSteps: + - template: /eng/pipelines/libraries/helix.yml + parameters: + creator: dotnet-bot + testRunNamePrefixSuffix: CoreCLR_WASI_$(_BuildConfig) + extraHelixArguments: $(_wasmRunSmokeTestsOnlyArg) ${{ parameters.extraHelixArguments }} + scenarios: ${{ parameters.scenarios }} diff --git a/eng/pipelines/runtime.yml b/eng/pipelines/runtime.yml index 77ec17437b8a1e..870fdb32116102 100644 --- a/eng/pipelines/runtime.yml +++ b/eng/pipelines/runtime.yml @@ -165,6 +165,16 @@ extends: - wasi_wasm alwaysRun: ${{ variables.isRollingBuild }} + # CoreCLR library-test smoke leg run on wasmtime via Helix + # (wasi_wasm queue). Starts with a single-library smoke set; see + # https://github.com/dotnet/runtime/issues/130129. + - template: /eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml + parameters: + platforms: + - wasi_wasm + shouldRunSmokeOnly: true + alwaysRun: ${{ variables.isRollingBuild }} + - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index ec824fe2f52f3d..e28553ab0b7d71 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -21,9 +21,22 @@ for WBT, and debugger tests --> + + + true + + <_AppArgs Condition="'$(WasmSingleFileBundle)' == 'true'">$([System.IO.Path]::GetFileNameWithoutExtension('$(WasmMainAssemblyFileName)')).wasm - <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true'">dotnet.wasm WasmTestRunner + + <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' != 'CoreCLR'">dotnet.wasm WasmTestRunner + <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">managed/corerun WasmTestRunner.dll <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmSingleFileBundle)' != 'true'">$(_AppArgs) managed/$(AssemblyName).dll <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmSingleFileBundle)' == 'true'">$(_AppArgs) $(AssemblyName).dll @@ -52,6 +65,11 @@ <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=hostcall-fuel=4294967295 <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1 + + <_XHarnessArgs Condition="'$(RuntimeFlavor)' == 'CoreCLR'">$(_XHarnessArgs) --engine-arg=-W --engine-arg=exceptions=y + <_XHarnessArgs Condition="'$(RuntimeFlavor)' == 'CoreCLR'">$(_XHarnessArgs) --engine-arg=--env --engine-arg=CORE_ROOT=/managed <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) <_InvariantGlobalization Condition="'$(InvariantGlobalization)' == 'true'">--env=DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true diff --git a/src/libraries/sendtohelix-wasi.targets b/src/libraries/sendtohelix-wasi.targets index 44500dfaad7b7b..52a00791267aac 100644 --- a/src/libraries/sendtohelix-wasi.targets +++ b/src/libraries/sendtohelix-wasi.targets @@ -32,6 +32,9 @@ true + + $(Scenario)-CLR-ST- wasmtime true @@ -124,12 +127,20 @@ + + + + + + diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 4d543e9028294d..f5bb4e5f998421 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -557,8 +557,8 @@ - - + + @@ -576,7 +576,11 @@ - + + + + diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets new file mode 100644 index 00000000000000..171ce4350682f7 --- /dev/null +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -0,0 +1,73 @@ + + + + + + + CoreCLR + false + + false + false + WasmTestRunner.dll + + + + + + + + + + + + + + + + + + + + + + <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)corerun" /> + <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)System.Private.CoreLib.dll" /> + + + + diff --git a/src/mono/wasi/build/WasiApp.InTree.props b/src/mono/wasi/build/WasiApp.InTree.props index db34cf23a2f5f9..66f09aa1b53023 100644 --- a/src/mono/wasi/build/WasiApp.InTree.props +++ b/src/mono/wasi/build/WasiApp.InTree.props @@ -1,6 +1,21 @@ + + + + false + + true + + + true + + AnyCPU false @@ -10,7 +25,7 @@ full false - + <_MonoRuntimeComponentDontLink Include="libmono-component-diagnostics_tracing-static.a"/> <_MonoRuntimeComponentDontLink Include="libmono-component-debugger-stub-static.a" /> <_MonoRuntimeComponentDontLink Include="libmono-component-hot_reload-stub-static.a" /> diff --git a/src/mono/wasi/build/WasiApp.InTree.targets b/src/mono/wasi/build/WasiApp.InTree.targets index 5161dead820985..f1bf0503fa4efd 100644 --- a/src/mono/wasi/build/WasiApp.InTree.targets +++ b/src/mono/wasi/build/WasiApp.InTree.targets @@ -7,7 +7,11 @@ - + + + Date: Tue, 14 Jul 2026 16:47:25 -0500 Subject: [PATCH 02/22] [wasi] Complete CoreCLR-WASI library-test bundle (framework + entry path) Follow-up fixes validated by a local wasi-wasm CoreCLR build, getting the smoke test through to xunit discovery. - tests.wasi.targets: corerun resolves the managed entry assembly relative to its own directory, so pass it as "managed/WasmTestRunner.dll". - WasiApp.CoreCLR.targets: the SDK does not resolve a CoreCLR runtime pack for the wasi-wasm RID, so the self-contained publish omits the framework assemblies (only the app's own closure is bundled). Copy the framework implementation assemblies from the runtime pack lib dir into managed/, alongside corerun and System.Private.CoreLib.dll, without overwriting app-published assemblies. With this the bundle boots CoreCLR under wasmtime and runs WasmTestRunner + the XHarness xunit runner through test discovery of System.Runtime.Tests. Remaining failures are runtime-side (interpreter precode/callhelpers), not test infra. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- eng/testing/tests.wasi.targets | 2 +- src/mono/wasi/build/WasiApp.CoreCLR.targets | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index e28553ab0b7d71..90cb1352b76544 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -36,7 +36,7 @@ CORE_ROOT, so it is bundled into managed/ alongside the framework (see WasiApp.CoreCLR.targets). Validated locally against the wasi-wasm runtime pack. --> <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' != 'CoreCLR'">dotnet.wasm WasmTestRunner - <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">managed/corerun WasmTestRunner.dll + <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">managed/corerun managed/WasmTestRunner.dll <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmSingleFileBundle)' != 'true'">$(_AppArgs) managed/$(AssemblyName).dll <_AppArgs Condition="'$(IsFunctionalTest)' != 'true' and '$(WasmSingleFileBundle)' == 'true'">$(_AppArgs) $(AssemblyName).dll diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 171ce4350682f7..e006584f7b0fef 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -54,9 +54,13 @@ runtime via CORE_ROOT, so it must sit in managed/ next to the framework assemblies (mirrors the CoreCLR-WASI runtime-test layout: corerun lives in CORE_ROOT). The run command in eng/testing/tests.wasi.targets uses "managed/corerun" with CORE_ROOT=/managed. - System.Private.CoreLib.dll ships in the pack's native/ dir (not lib/), so it is not - bundled by WasiAppBuilder and must be copied alongside corerun. Validated locally - against the wasi-wasm runtime pack under wasmtime. --> + + The SDK does not resolve a CoreCLR runtime pack for the wasi-wasm RID, so the + self-contained publish does not copy the framework assemblies (System.Runtime.dll, + System.Collections.dll, ...) - only the app's own closure lands in the bundle. Copy + the framework from the runtime pack's lib dir, plus corerun and System.Private.CoreLib.dll + (which ship in the pack's native/ dir), into managed/. Validated locally against the + wasi-wasm runtime pack under wasmtime. See https://github.com/dotnet/runtime/issues/130129. --> @@ -65,9 +69,16 @@ <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)corerun" /> <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)System.Private.CoreLib.dll" /> + + <_WasiCoreCLRFrameworkFiles Include="$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)\*.dll" /> + + From 086c15596d2bfb6a666c8afcdd3c47e68bdf9f87 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 16:49:16 -0500 Subject: [PATCH 03/22] [wasi] Note #130634 as the known blocker for the CoreCLR-WASI smoke leg The CoreCLR-WASI library-test smoke leg boots and reaches xunit discovery, but traps in the interpreter on a cold call through a portable entry point with a mismatched wasm function signature (dotnet/runtime#130634), which has no merged fix yet. Document that the leg is expected to stay red until #130634 lands. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../common/templates/wasi-wasm-coreclr-library-tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml index 48f1282394126c..168715c6c59487 100644 --- a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -18,6 +18,11 @@ jobs: # with the WasmTestOnWasmtime scenario and the wasi_wasm helix queue. # See https://github.com/dotnet/runtime/issues/130129. # +# Bring-up leg: smoke-only and non-gating (rolling alwaysRun). It is expected to remain +# red until the CoreCLR-WASI cold portable-entry-point signature-mismatch fix lands +# (https://github.com/dotnet/runtime/issues/130634); any test that formats an exception +# message or hits cold globalization/reflection traps in the interpreter until then. +# - template: /eng/pipelines/common/platform-matrix.yml parameters: jobTemplate: /eng/pipelines/common/global-build-job.yml From e8fcde79cb7ea68f01e5e351bd35cfbf05e34b49 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 17:53:00 -0500 Subject: [PATCH 04/22] [wasi] Correct CoreCLR-WASI smoke leg known-blocker note The interp smoke blocker was the nested-type reverse-thunk key mismatch (#130739), not the cold portable-entry-point timing (#130634). The leg needs the per-app relink for thunk coverage; #130634 only affects a future R2R-enabled leg. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../templates/wasi-wasm-coreclr-library-tests.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml index 168715c6c59487..e400ae39721413 100644 --- a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -18,10 +18,12 @@ jobs: # with the WasmTestOnWasmtime scenario and the wasi_wasm helix queue. # See https://github.com/dotnet/runtime/issues/130129. # -# Bring-up leg: smoke-only and non-gating (rolling alwaysRun). It is expected to remain -# red until the CoreCLR-WASI cold portable-entry-point signature-mismatch fix lands -# (https://github.com/dotnet/runtime/issues/130634); any test that formats an exception -# message or hits cold globalization/reflection traps in the interpreter until then. +# Bring-up leg: smoke-only and non-gating (rolling alwaysRun). It stays red until the +# per-app native relink lands (so a test's own [UnmanagedCallersOnly] reverse thunks are +# generated + linked into corerun); see https://github.com/dotnet/runtime/issues/130129. +# With the relink + the nested-type thunk-key fix (dotnet/runtime#130739), the interpreter +# runs the suite. #130634 (cold portable-entry-point publication) only affects a future +# R2R-enabled leg, not this pure-interpreter smoke. # - template: /eng/pipelines/common/platform-matrix.yml parameters: From 0ba4ece5402e99c77dffba277ab68967407b9a15 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 18:26:58 -0500 Subject: [PATCH 05/22] [wasi] Add WasiHost-Static archive for CoreCLR-WASI per-app relink Build the corerun host as a static archive (libWasiHost.a) alongside the executable, mirroring the browser BrowserHost-Static / libBrowserHost.a pattern. A per-app relink (WasiApp.CoreCLR.targets) links this archive with the app-generated callhelpers (replacing the baked coreclr_gen_static) so library/test [UnmanagedCallersOnly] reverse thunks are covered. Adds the libWasiHost.a platform-manifest entry so it ships in the runtime pack native dir. See https://github.com/dotnet/runtime/issues/130129. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/coreclr/hosts/corerun/CMakeLists.txt | 19 +++++++++++++++++++ .../Directory.Build.props | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index a4f9237fa7c95a..3adb07476cebd3 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -142,6 +142,25 @@ endif() install_clr(TARGETS corerun DESTINATIONS . COMPONENT hosts) +# WASI: also build the host as a STATIC archive (libWasiHost.a) so a per-app relink can +# re-link corerun with app-generated callhelpers (replacing the baked coreclr_gen_static), +# mirroring the browser BrowserHost-Static / libBrowserHost.a pattern used by +# BrowserWasmApp.CoreCLR.targets. The runtime static libraries (coreclr_static, System.Native, +# ...) are NOT linked here; they are supplied at relink time from the runtime pack. +# See https://github.com/dotnet/runtime/issues/130129. +if (CLR_CMAKE_TARGET_WASI) + add_library(WasiHost-Static STATIC + corerun.cpp + dotenv.cpp + ./wasm/pinvoke_override.cpp + native.rc + ) + set_target_properties(WasiHost-Static PROPERTIES OUTPUT_NAME WasiHost CLEAN_DIRECT_OUTPUT 1) + target_include_directories(WasiHost-Static PRIVATE ./wasm/) + target_link_libraries(WasiHost-Static PRIVATE minipal) + install_clr(TARGETS WasiHost-Static DESTINATIONS sharedFramework COMPONENT runtime) +endif() + # If there's a dynamic ASAN runtime, then install it in the directory where we put our executable. if (NOT "${ASAN_RUNTIME}" STREQUAL "") install(FILES ${ASAN_RUNTIME} DESTINATION .) diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 3720e00b56f0c7..ccf3ca0613d2ec 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -271,6 +271,8 @@ + + From e5386ccf5278044d24f5e8ddf276765245957370 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 18:35:44 -0500 Subject: [PATCH 06/22] [wasi] Relink corerun per-app in WasiApp.CoreCLR.targets Replace the prebuilt-corerun copy with a per-app native relink so library/test [UnmanagedCallersOnly] reverse thunks are covered (mirrors the browser CoreCLR relink). The target: 1. runs ManagedToNativeGenerator (TargetOS=wasi) over the bundled managed closure to emit the three callhelper .cpp tables, 2. compiles them with the wasi-sdk clang against the curated header set (force-included coreclr_compat.h + vm/wasm + minipal includes, shared with the browser CoreCLR build), and 3. relinks corerun from libWasiHost.a + the runtime-pack static archives + the freshly compiled app callhelper .o (dropping the baked libcoreclr_gen_static.a) via wasm-component-ld, declaring the wasi:http world with -Wl,--component-type,WasiHttpWorld_component_type.wit. The link order (incl. the duplicated libminipal.a and mid-list -lstdc++) mirrors the corerun CMake link line, which was validated end-to-end by a manual relink (9930 tests discovered and running under wasmtime -S http). Falls back to copying the prebuilt corerun when WasiCoreCLRRelinkCoreRun=false. See https://github.com/dotnet/runtime/issues/130129. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 271 ++++++++++++++++++-- 1 file changed, 244 insertions(+), 27 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index e006584f7b0fef..67e04a4542f3a7 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -3,35 +3,55 @@ CoreCLR-WASI app-builder targets. Sibling of the Mono WasiApp.targets, mirroring the browser split (BrowserWasmApp.targets vs BrowserWasmApp.CoreCLR.targets). - Unlike Mono - which relinks dotnet.wasm per-app with wasi-sdk clang - CoreCLR-WASI - ships a prebuilt corerun host in the runtime pack and loads managed assemblies - from the wasmtime filesystem. So there is no per-app native relink: we reuse the - shared WASI bundling (WasiAppBuilder + runtimeconfig + run script) with - WasmBuildNative disabled, and only swap the native host asset. - - TODO (CI validation, #130129): the exact native-asset set (host filename, coreclr - native libraries, icu) must be confirmed against the built CoreCLR wasi-wasm runtime - pack layout. Validated locally against the wasi-wasm runtime pack (2026-07): the host - is named "corerun" and runs under wasmtime with -W exceptions=y. + Like the browser CoreCLR path, this performs a per-app native relink of the corerun + host so that the reverse P/Invoke thunks for the app/test [UnmanagedCallersOnly] + methods are covered. The baked callhelpers table (libcoreclr_gen_static.a in the + runtime pack) only covers framework top-level UCO callbacks; library/test callbacks + (e.g. a nested WindowsUILanguageHelper.EnumUiLanguagesCallback) are unknown to it and + would trap at first call. So we: + + 1. run ManagedToNativeGenerator (TargetOS=wasi) over the bundle assemblies to emit + callhelpers-{pinvoke,reverse,interp-to-managed}.cpp, + 2. compile them with the wasi-sdk clang, and + 3. relink corerun from the runtime-pack static archives (libWasiHost.a + the CoreCLR + and platform .a files) PLUS the freshly compiled app callhelper .o, dropping the + baked libcoreclr_gen_static.a (the app .o replaces it), via wasm-component-ld. + + The wasi:http component import that System.Net.* [WasmImportLinkage] bindings pull in + is declared to wasm-component-ld via the component-type linker flag pointing at + WasiHttpWorld_component_type.wit (mirrors Mono's WasiApp.targets); wasmtime is given + "-S http" at run time by eng/testing/tests.wasi.targets. + + See https://github.com/dotnet/runtime/issues/130129. --> - + + CoreCLR false - + false false WasmTestRunner.dll + + true + dotnet.wasm. For the CoreCLR bring-up we run invariant-globalization by default, so + no ICU data is bundled; corerun is placed into managed/ by the relink/copy below. --> @@ -44,7 +64,7 @@ - @@ -57,22 +77,22 @@ The SDK does not resolve a CoreCLR runtime pack for the wasi-wasm RID, so the self-contained publish does not copy the framework assemblies (System.Runtime.dll, - System.Collections.dll, ...) - only the app's own closure lands in the bundle. Copy - the framework from the runtime pack's lib dir, plus corerun and System.Private.CoreLib.dll - (which ship in the pack's native/ dir), into managed/. Validated locally against the - wasi-wasm runtime pack under wasmtime. See https://github.com/dotnet/runtime/issues/130129. --> - + - + - <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)corerun" /> - <_WasiCoreCLRHostFiles Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)System.Private.CoreLib.dll" /> + <_WasiCoreCLRCoreLib Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)System.Private.CoreLib.dll" /> <_WasiCoreCLRFrameworkFiles Include="$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)\*.dll" /> - @@ -81,4 +101,201 @@ Condition="!Exists('$(WasmAppDir)managed\%(_WasiCoreCLRFrameworkFiles.FileName)%(_WasiCoreCLRFrameworkFiles.Extension)')" SkipUnchangedFiles="true" /> + + + + + + + + + + + + + + + <_WasiRelinkObjDir>$([MSBuild]::NormalizeDirectory('$(_WasmIntermediateOutputPath)', 'corerun-relink')) + <_WasiClangxx>$(WASI_SDK_PATH)bin/clang++ + <_WasiClangxx Condition="$([MSBuild]::IsOSPlatform('Windows'))">$(_WasiClangxx).exe + <_WasiComponentLd>$(WasiSdkBinPath)wasm-component-ld + <_WasiComponentLd Condition="$([MSBuild]::IsOSPlatform('Windows'))">$(_WasiComponentLd).exe + <_WasiSysroot>$(WASI_SDK_PATH)share/wasi-sysroot + + <_WasiPInvokeTablePath>$(_WasiRelinkObjDir)callhelpers-pinvoke.cpp + <_WasiReversePInvokeTablePath>$(_WasiRelinkObjDir)callhelpers-reverse.cpp + <_WasiInterpToNativeTablePath>$(_WasiRelinkObjDir)callhelpers-interp-to-managed.cpp + <_WasiM2NCachePath>$(_WasiRelinkObjDir)m2n_cache.txt + + + <_WasiCoreClrCompatHeader>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', '..', 'browser', 'build', 'coreclr_compat.h')) + <_WasiVmWasmIncludeDir Condition="'$(CORECLR_VM_WASM_INCLUDE_DIR)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$(CORECLR_VM_WASM_INCLUDE_DIR)')))) + <_WasiVmWasmIncludeDir Condition="'$(_WasiVmWasmIncludeDir)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..', '..', '..', 'coreclr', 'vm', 'wasm')) + <_WasiMinipalIncludeRoot Condition="'$(MINIPAL_INCLUDE_DIR)' != ''">$([MSBuild]::NormalizeDirectory($([System.IO.Path]::GetFullPath('$(MINIPAL_INCLUDE_DIR)')), '..')) + <_WasiMinipalIncludeRoot Condition="'$(_WasiMinipalIncludeRoot)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..', '..', '..', 'native')) + + + <_WasiHttpWorldWit>$(MicrosoftNetCoreAppRuntimePackRidNativeDir)WasiHttpWorld_component_type.wit + <_WasiHttpWorldWit Condition="!Exists('$(_WasiHttpWorldWit)')">$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..', '..', '..', 'libraries', 'System.Net.Http', 'src', 'System', 'Net', 'Http', 'WasiHttpHandler', 'WasiHttpWorld_component_type.wit')) + + <_WasiRelinkCompileRsp>$(_WasiRelinkObjDir)compile.rsp + <_WasiRelinkLinkRsp>$(_WasiRelinkObjDir)link.rsp + <_WasiRelinkOptFlag Condition="'$(Configuration)' == 'Debug'">-O1 + <_WasiRelinkOptFlag Condition="'$(_WasiRelinkOptFlag)' == ''">-O2 + + + + + + + + + + <_WasiManagedAssemblies Include="$(WasmAppDir)managed\*.dll" /> + + <_WasiPInvokeModules Include="libSystem.Native" /> + <_WasiPInvokeModules Include="libSystem.IO.Compression.Native" /> + <_WasiPInvokeModules Include="libSystem.Globalization.Native" Condition="'$(InvariantGlobalization)' != 'true'" /> + <_WasiIgnoredPInvokeModules Include="libSystem.Globalization.Native" Condition="'$(InvariantGlobalization)' == 'true'" /> + + + + + + + + + <_WasiRelinkCompileFlags Include="--target=wasm32-wasip2" /> + <_WasiRelinkCompileFlags Include="--sysroot="$(_WasiSysroot.Replace('\','/'))"" /> + <_WasiRelinkCompileFlags Include="$(_WasiRelinkOptFlag)" /> + <_WasiRelinkCompileFlags Include="-DNDEBUG" Condition="'$(Configuration)' != 'Debug'" /> + <_WasiRelinkCompileFlags Include="-std=gnu++17" /> + <_WasiRelinkCompileFlags Include="-fwasm-exceptions" /> + <_WasiRelinkCompileFlags Include="-mllvm" /> + <_WasiRelinkCompileFlags Include="-wasm-use-legacy-eh=false" /> + <_WasiRelinkCompileFlags Include="-fno-rtti" /> + + <_WasiRelinkCompileFlags Include="-DGEN_PINVOKE=1" /> + <_WasiRelinkCompileFlags Include="-DHOST_WASM" /> + <_WasiRelinkCompileFlags Include="-DHOST_UNIX" /> + <_WasiRelinkCompileFlags Include="-DHOST_32BIT=1" /> + <_WasiRelinkCompileFlags Include="-DTARGET_WASM" /> + <_WasiRelinkCompileFlags Include="-DTARGET_WASM32" /> + <_WasiRelinkCompileFlags Include="-DTARGET_WASI" /> + <_WasiRelinkCompileFlags Include="-DTARGET_UNIX" /> + <_WasiRelinkCompileFlags Include="-DTARGET_32BIT" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_INTERPRETER" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_PORTABLE_ENTRYPOINTS" /> + <_WasiRelinkCompileFlags Include="-include "$(_WasiCoreClrCompatHeader.Replace('\','/'))"" /> + <_WasiRelinkCompileFlags Include="-I"$(_WasiVmWasmIncludeDir.TrimEnd('\/').Replace('\','/'))"" /> + <_WasiRelinkCompileFlags Include="-I"$(_WasiMinipalIncludeRoot.TrimEnd('\/').Replace('\','/'))"" /> + + + + + <_WasiCallHelperSource Include="$(_WasiPInvokeTablePath)" ObjectFile="$(_WasiRelinkObjDir)callhelpers-pinvoke.o" /> + <_WasiCallHelperSource Include="$(_WasiReversePInvokeTablePath)" ObjectFile="$(_WasiRelinkObjDir)callhelpers-reverse.o" /> + <_WasiCallHelperSource Include="$(_WasiInterpToNativeTablePath)" ObjectFile="$(_WasiRelinkObjDir)callhelpers-interp-to-managed.o" /> + + + + + + + + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libWasiHost.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libminipal.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclr_static.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.IO.Compression.Native.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.a" /> + <_WasiHostLibsPre Condition="'$(InvariantTimezone)' == 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.TimeZoneData.Invariant.a" /> + <_WasiHostLibsPre Condition="'$(InvariantTimezone)' != 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Native.TimeZoneData.a" /> + + <_WasiHostLibsPre Include="%(_WasiCallHelperSource.ObjectFile)" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libnativeresourcestring.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicudata.a" /> + + + <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclrminipal.a" /> + <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclrpal.a" /> + <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libminipal.a" /> + <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libz.a" /> + <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libzstd.a" Condition="Exists('$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libzstd.a')" /> + + + + <_WasiRelinkOutput>$(WasmAppDir)managed\corerun + + + + <_WasiRelinkLinkFlags Include="--target=wasm32-wasip2" /> + <_WasiRelinkLinkFlags Include="$(_WasiRelinkOptFlag)" /> + <_WasiRelinkLinkFlags Include="-DNDEBUG" Condition="'$(Configuration)' != 'Debug'" /> + <_WasiRelinkLinkFlags Include="-Wl,--gc-sections" /> + <_WasiRelinkLinkFlags Include="-fwasm-exceptions" /> + <_WasiRelinkLinkFlags Include="-mllvm" /> + <_WasiRelinkLinkFlags Include="-wasm-use-legacy-eh=false" /> + <_WasiRelinkLinkFlags Include="-Wno-unused-command-line-argument" /> + <_WasiRelinkLinkFlags Include="-Wl,--error-limit=0" /> + <_WasiRelinkLinkFlags Include="-lc -lc++ -lc++abi -lunwind -ldl" /> + <_WasiRelinkLinkFlags Include="-lwasi-emulated-process-clocks -lwasi-emulated-signal -lwasi-emulated-mman -lwasi-emulated-getpid" /> + <_WasiRelinkLinkFlags Include="-fuse-ld="$(_WasiComponentLd.Replace('\','/'))"" /> + <_WasiRelinkLinkFlags Include="-Wl,-z,stack-size=8388608" /> + <_WasiRelinkLinkFlags Include="-Wl,--initial-memory=134217728" /> + <_WasiRelinkLinkFlags Include="-Wl,--max-memory=4294967296" /> + <_WasiRelinkLinkFlags Include="-Wl,--component-type,"$(_WasiHttpWorldWit.Replace('\','/'))"" /> + <_WasiRelinkLinkFlags Include="@(_WasiHostLibsPre->'"%(Identity)"')" /> + <_WasiRelinkLinkFlags Include="-lstdc++" /> + <_WasiRelinkLinkFlags Include="@(_WasiHostLibsPost->'"%(Identity)"')" /> + <_WasiRelinkLinkFlags Include="-o "$(_WasiRelinkOutput.Replace('\','/'))"" /> + + + + + + + + + + + From a662aca1f24d8a9fb60b93debb54378a20298768 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 18:53:09 -0500 Subject: [PATCH 07/22] [wasi] Fix corerun relink: whole-archive host + full VM define set Two corrections after validating the relink end-to-end under wasmtime: - Wrap libWasiHost.a in --whole-archive/--no-whole-archive so main()/_start are pulled in. wasm-ld only includes archive members that satisfy a strong undefined reference, but main is referenced weakly by __main_void, so a plain archive link left main as undefined_weak and the host trapped at entry. - Compile the generated callhelpers with the full CoreCLR-WASI VM define set (from the runtime callhelper compile) rather than a sparse subset, so they build against the same ABI/struct layout as libcoreclr_static.a. With these, the per-app relinked corerun boots, coreclr runs, and xunit discovers and executes 9930 System.Runtime.Tests cases under 'wasmtime -W exceptions=y -S http' (managed exception dispatch through the interp EH + reverse UCO thunks works; remaining [FAIL]s are invariant-mode CultureNotFoundException test outcomes, not crashes). See https://github.com/dotnet/runtime/issues/130129. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 58 +++++++++++++++++---- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 67e04a4542f3a7..cc00b3eb7b2fdc 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -202,18 +202,50 @@ <_WasiRelinkCompileFlags Include="-mllvm" /> <_WasiRelinkCompileFlags Include="-wasm-use-legacy-eh=false" /> <_WasiRelinkCompileFlags Include="-fno-rtti" /> - + <_WasiRelinkCompileFlags Include="-DGEN_PINVOKE=1" /> - <_WasiRelinkCompileFlags Include="-DHOST_WASM" /> - <_WasiRelinkCompileFlags Include="-DHOST_UNIX" /> + <_WasiRelinkCompileFlags Include="-DCOMPILER_SUPPORTS_W_RESERVED_IDENTIFIER" /> + <_WasiRelinkCompileFlags Include="-DDEBUGGING_SUPPORTED" /> + <_WasiRelinkCompileFlags Include="-DDISABLE_CONTRACTS" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_CACHED_INTERFACE_DISPATCH" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_COLLECTIBLE_TYPES" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_CORECLR" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_DBGIPC_TRANSPORT_DI" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_DBGIPC_TRANSPORT_VM" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_DEFAULT_INTERFACES" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_INTERPRETER" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_MULTICOREJIT" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_PAL_ANSI" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_PORTABLE_ENTRYPOINTS" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_PORTABLE_HELPERS" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_READYTORUN" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_REMOTE_PROC_MEM" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_STATICALLY_LINKED" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_SYMDIFF" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_USE_ASM_GC_WRITE_BARRIERS" /> + <_WasiRelinkCompileFlags Include="-DFEATURE_WEBCIL" /> <_WasiRelinkCompileFlags Include="-DHOST_32BIT=1" /> + <_WasiRelinkCompileFlags Include="-DHOST_UNIX" /> + <_WasiRelinkCompileFlags Include="-DHOST_WASM" /> + <_WasiRelinkCompileFlags Include="-DTARGET_32BIT" /> + <_WasiRelinkCompileFlags Include="-DTARGET_UNIX" /> + <_WasiRelinkCompileFlags Include="-DTARGET_WASI" /> <_WasiRelinkCompileFlags Include="-DTARGET_WASM" /> <_WasiRelinkCompileFlags Include="-DTARGET_WASM32" /> - <_WasiRelinkCompileFlags Include="-DTARGET_WASI" /> - <_WasiRelinkCompileFlags Include="-DTARGET_UNIX" /> - <_WasiRelinkCompileFlags Include="-DTARGET_32BIT" /> - <_WasiRelinkCompileFlags Include="-DFEATURE_INTERPRETER" /> - <_WasiRelinkCompileFlags Include="-DFEATURE_PORTABLE_ENTRYPOINTS" /> + <_WasiRelinkCompileFlags Include="-DUNICODE" /> + <_WasiRelinkCompileFlags Include="-D_UNICODE" /> + <_WasiRelinkCompileFlags Include="-DURTBLDENV_FRIENDLY=Retail" /> + <_WasiRelinkCompileFlags Include="-D_FILE_OFFSET_BITS=64" /> + <_WasiRelinkCompileFlags Include="-D_TIME_BITS=64" /> + <_WasiRelinkCompileFlags Include="-D_SECURE_SCL=0" /> + <_WasiRelinkCompileFlags Include="-D_WASI_EMULATED_GETPID" /> + <_WasiRelinkCompileFlags Include="-D_WASI_EMULATED_MMAN" /> + <_WasiRelinkCompileFlags Include="-D_WASI_EMULATED_PROCESS_CLOCKS" /> + <_WasiRelinkCompileFlags Include="-D_WASI_EMULATED_SIGNAL" /> <_WasiRelinkCompileFlags Include="-include "$(_WasiCoreClrCompatHeader.Replace('\','/'))"" /> <_WasiRelinkCompileFlags Include="-I"$(_WasiVmWasmIncludeDir.TrimEnd('\/').Replace('\','/'))"" /> <_WasiRelinkCompileFlags Include="-I"$(_WasiMinipalIncludeRoot.TrimEnd('\/').Replace('\','/'))"" /> @@ -237,8 +269,10 @@ always linked into the host (invariant is a run-time config + a generator ignore, not a host link-time exclusion). --> - - <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libWasiHost.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libminipal.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclr_static.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.IO.Compression.Native.a" /> @@ -282,6 +316,10 @@ <_WasiRelinkLinkFlags Include="-Wl,--initial-memory=134217728" /> <_WasiRelinkLinkFlags Include="-Wl,--max-memory=4294967296" /> <_WasiRelinkLinkFlags Include="-Wl,--component-type,"$(_WasiHttpWorldWit.Replace('\','/'))"" /> + + <_WasiRelinkLinkFlags Include="-Wl,--whole-archive" /> + <_WasiRelinkLinkFlags Include=""$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libWasiHost.a"" /> + <_WasiRelinkLinkFlags Include="-Wl,--no-whole-archive" /> <_WasiRelinkLinkFlags Include="@(_WasiHostLibsPre->'"%(Identity)"')" /> <_WasiRelinkLinkFlags Include="-lstdc++" /> <_WasiRelinkLinkFlags Include="@(_WasiHostLibsPost->'"%(Identity)"')" /> From 914362d1034c3bc1a2d16fc01da9389f80b9cb01 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 18:54:49 -0500 Subject: [PATCH 08/22] [wasi] Update CoreCLR-WASI notes for the per-app corerun relink Refresh the stale 'prebuilt corerun' comments in tests.wasi.targets and the CI template now that WasiApp.CoreCLR.targets relinks corerun per-app (validated locally: discovers + runs the full System.Runtime.Tests suite under wasmtime). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../templates/wasi-wasm-coreclr-library-tests.yml | 12 ++++++------ eng/testing/tests.wasi.targets | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml index e400ae39721413..93e037f0732bde 100644 --- a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -18,12 +18,12 @@ jobs: # with the WasmTestOnWasmtime scenario and the wasi_wasm helix queue. # See https://github.com/dotnet/runtime/issues/130129. # -# Bring-up leg: smoke-only and non-gating (rolling alwaysRun). It stays red until the -# per-app native relink lands (so a test's own [UnmanagedCallersOnly] reverse thunks are -# generated + linked into corerun); see https://github.com/dotnet/runtime/issues/130129. -# With the relink + the nested-type thunk-key fix (dotnet/runtime#130739), the interpreter -# runs the suite. #130634 (cold portable-entry-point publication) only affects a future -# R2R-enabled leg, not this pure-interpreter smoke. +# Bring-up leg: smoke-only and non-gating (rolling alwaysRun). WasiApp.CoreCLR.targets +# performs a per-app native relink so a test's own [UnmanagedCallersOnly] reverse thunks +# are generated + linked into corerun; with that relink + the nested-type thunk-key fix +# (dotnet/runtime#130739) the interpreter discovers and runs the suite (validated locally: +# 9930 System.Runtime.Tests cases). #130634 (cold portable-entry-point publication) only +# affects a future R2R-enabled leg, not this pure-interpreter smoke. # - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index 90cb1352b76544..ebffc594363b34 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -21,9 +21,9 @@ for WBT, and debugger tests --> - + true @@ -31,10 +31,10 @@ <_AppArgs Condition="'$(WasmSingleFileBundle)' == 'true'">$([System.IO.Path]::GetFileNameWithoutExtension('$(WasmMainAssemblyFileName)')).wasm + per-app relinked corerun host with the managed entry assembly (.dll). corerun is + relinked into managed/ alongside the framework and finds the runtime via CORE_ROOT + (see WasiApp.CoreCLR.targets). Validated locally: discovers + runs the full + System.Runtime.Tests suite under wasmtime. --> <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' != 'CoreCLR'">dotnet.wasm WasmTestRunner <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">managed/corerun managed/WasmTestRunner.dll From c45c22230a15ceb3853ec0f9e7e9bd56756df2c6 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 19:08:49 -0500 Subject: [PATCH 09/22] [wasi] Document why the corerun relink omits libgcinfo_unix_wasm.a The browser CoreCLR relink links libgcinfo_unix_wasm.a; the wasi relink deliberately does not, mirroring the authoritative wasi corerun CMake link line. Verified: that archive exports only the gcinfo ENCODER helpers (GcInfoArrayList, used when emitting GC info in JIT/crossgen), which the interpreter host never does; the gcinfo DECODER used by runtime GC stackwalks (TGcInfoDecoder) is folded into libcoreclr_static.a as weak symbols. corerun.exe omits the separate archive and runs the full CoreCLR-WASI runtime suite, confirming it is unneeded for the host. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index cc00b3eb7b2fdc..4952e94b3316b1 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -282,6 +282,12 @@ <_WasiHostLibsPre Include="%(_WasiCallHelperSource.ObjectFile)" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libnativeresourcestring.a" /> + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> From fa09c9b9e9b2af89e324faa6b9f753a698d483a2 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 19:13:32 -0500 Subject: [PATCH 10/22] [wasi] Refine libgcinfo_unix_wasm.a omission rationale Note the precise mechanism: libcoreclr_static.a does carry undefined references to the gcinfo encoder helpers (GcInfoArrayList), but they live in the emit path the interp/R2R host never reaches, so the linker gc-sections pass drops them and a successful link proves no retained code needs them. The shipped wasi corerun executable links identically (without the archive) and runs the full runtime suite. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 4952e94b3316b1..da7a33e97bd844 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -282,12 +282,16 @@ <_WasiHostLibsPre Include="%(_WasiCallHelperSource.ObjectFile)" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libnativeresourcestring.a" /> - + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> From 985e1498f5c420fe6507f076450beaf0a97d5a89 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 19:15:17 -0500 Subject: [PATCH 11/22] [wasi] Document coreclr_compat.h cross-tree reference as intentional Reference the browser build's coreclr_compat.h directly rather than copying it: a second copy would drift from the callhelpers ABI (the class of bug the nested-key fix addressed). Note the cross-tree reference is intentional and temporary; relocating the header to a neutral coreclr-adjacent location shared by browser + wasi, plus out-of-tree staging, is tracked with #128362. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index da7a33e97bd844..2399d2643bc6d1 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -140,7 +140,13 @@ _EmccCFlagsGenerated approach): force-include coreclr_compat.h (shared with the browser CoreCLR build) and add -I for the dir that has and the minipal root. In-tree these are the repo source dirs; env-var overrides - let them resolve from a shipped payload out-of-tree. --> + let them resolve from a shipped payload out-of-tree. + coreclr_compat.h is referenced directly from the browser build dir (NOT copied) + on purpose: it must stay in lockstep with the callhelpers ABI, and a second copy + would invite exactly the kind of silent drift the nested-key bug came from. This + cross-tree reference is intentional and temporary; relocating the header to a + neutral coreclr-adjacent location shared by both browser and wasi (and staging it + + the vm/minipal headers for out-of-tree runs) is tracked with #128362. --> <_WasiCoreClrCompatHeader>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', '..', 'browser', 'build', 'coreclr_compat.h')) <_WasiVmWasmIncludeDir Condition="'$(CORECLR_VM_WASM_INCLUDE_DIR)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$(CORECLR_VM_WASM_INCLUDE_DIR)')))) <_WasiVmWasmIncludeDir Condition="'$(_WasiVmWasmIncludeDir)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..', '..', '..', 'coreclr', 'vm', 'wasm')) From bb8ddb7c50bffd1000d391760cbe5852fd625789 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 20:08:10 -0500 Subject: [PATCH 12/22] [wasi] Tighten WasiApp.CoreCLR.targets comments to match surrounding style Compress the paragraph-length explanatory comments (relink overview, framework copy, coreclr_compat.h, gcinfo, whole-archive, define set) to the terse why-style used across the wasm build targets. Comment ratio drops from ~31% to ~23% (below the browser CoreCLR targets' 27%); load-bearing rationale (whole-archive pulls main, gcinfo encoder-vs-decoder, full define set for ABI parity) is kept, just shorter. Comment-only; relink still builds and runs the full System.Runtime.Tests suite under wasmtime. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 118 +++++++------------- 1 file changed, 41 insertions(+), 77 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 2399d2643bc6d1..6d905de1dacdfa 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -1,33 +1,22 @@ - @@ -64,24 +53,14 @@ - + - + @@ -136,17 +115,12 @@ <_WasiInterpToNativeTablePath>$(_WasiRelinkObjDir)callhelpers-interp-to-managed.cpp <_WasiM2NCachePath>$(_WasiRelinkObjDir)m2n_cache.txt - + <_WasiCoreClrCompatHeader>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', '..', 'browser', 'build', 'coreclr_compat.h')) <_WasiVmWasmIncludeDir Condition="'$(CORECLR_VM_WASM_INCLUDE_DIR)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$(CORECLR_VM_WASM_INCLUDE_DIR)')))) <_WasiVmWasmIncludeDir Condition="'$(_WasiVmWasmIncludeDir)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..', '..', '..', 'coreclr', 'vm', 'wasm')) @@ -208,10 +182,9 @@ <_WasiRelinkCompileFlags Include="-mllvm" /> <_WasiRelinkCompileFlags Include="-wasm-use-legacy-eh=false" /> <_WasiRelinkCompileFlags Include="-fno-rtti" /> - + <_WasiRelinkCompileFlags Include="-DGEN_PINVOKE=1" /> <_WasiRelinkCompileFlags Include="-DCOMPILER_SUPPORTS_W_RESERVED_IDENTIFIER" /> <_WasiRelinkCompileFlags Include="-DDEBUGGING_SUPPORTED" /> @@ -267,18 +240,14 @@ - + - + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libminipal.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclr_static.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.IO.Compression.Native.a" /> @@ -288,16 +257,11 @@ <_WasiHostLibsPre Include="%(_WasiCallHelperSource.ObjectFile)" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libnativeresourcestring.a" /> - + <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> From 61dbe80a2d46873f5541b41aa07835a16fbda11a Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 20:24:42 -0500 Subject: [PATCH 13/22] [wasi] Address PR review: correct stale 'prebuilt corerun' comments + issue link Copilot PR review flagged leftover comments from the earlier prebuilt-corerun approach. CoreCLR-WASI now relinks the corerun host per-app; update the comments in WasiApp.InTree.props/.targets and sendtohelix-wasi.targets accordingly. Also fix the CI template to reference #130740 (the nested-type thunk-key fix) instead of #130739 (which tracks the remaining enclosing-type collision limitation). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../common/templates/wasi-wasm-coreclr-library-tests.yml | 9 +++++---- src/libraries/sendtohelix-wasi.targets | 5 +++-- src/mono/wasi/build/WasiApp.InTree.props | 5 +++-- src/mono/wasi/build/WasiApp.InTree.targets | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml index 93e037f0732bde..05c425aa861cc6 100644 --- a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -20,10 +20,11 @@ jobs: # # Bring-up leg: smoke-only and non-gating (rolling alwaysRun). WasiApp.CoreCLR.targets # performs a per-app native relink so a test's own [UnmanagedCallersOnly] reverse thunks -# are generated + linked into corerun; with that relink + the nested-type thunk-key fix -# (dotnet/runtime#130739) the interpreter discovers and runs the suite (validated locally: -# 9930 System.Runtime.Tests cases). #130634 (cold portable-entry-point publication) only -# affects a future R2R-enabled leg, not this pure-interpreter smoke. +# are generated + linked into corerun; with that relink + the nested-type reverse-thunk +# key fix (also filed standalone as dotnet/runtime#130740; the remaining enclosing-type +# collision limitation is tracked by #130739) the interpreter discovers and runs the suite +# (validated locally: 9930 System.Runtime.Tests cases). #130634 (cold portable-entry-point +# publication) only affects a future R2R-enabled leg, not this pure-interpreter smoke. # - template: /eng/pipelines/common/platform-matrix.yml parameters: diff --git a/src/libraries/sendtohelix-wasi.targets b/src/libraries/sendtohelix-wasi.targets index 52a00791267aac..daaef76325e4d7 100644 --- a/src/libraries/sendtohelix-wasi.targets +++ b/src/libraries/sendtohelix-wasi.targets @@ -130,8 +130,9 @@ + build does not produce these, and the CoreCLR-WASI corerun relink happens at + build time with the wasi-sdk (not the Mono AOT/cross tooling), so these Mono + runtime payloads are not needed on Helix. --> diff --git a/src/mono/wasi/build/WasiApp.InTree.props b/src/mono/wasi/build/WasiApp.InTree.props index 66f09aa1b53023..c9da92ff307c4d 100644 --- a/src/mono/wasi/build/WasiApp.InTree.props +++ b/src/mono/wasi/build/WasiApp.InTree.props @@ -3,8 +3,9 @@ + flavor. Unlike Mono (which relinks dotnet.wasm per-app), CoreCLR-WASI uses the + corerun host, relinked per-app to cover app/test reverse thunks (see + WasiApp.CoreCLR.targets). --> false + per-app via wasi-sdk; CoreCLR relinks the corerun host per-app (WasiApp.CoreCLR.targets). --> From e8e4b77cb17225ae2257cf658b8779db451c8120 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 20:40:30 -0500 Subject: [PATCH 14/22] [wasi] Address PR review round 2: target triple + Copy batching - Use the full --target=wasm32-unknown-wasip2 triple (matching the sibling Mono WasiApp.targets) for the callhelper compile and the corerun link, instead of the wasm32-wasip2 alias. Clang-equivalent (vendor defaults to unknown) so ABI-identical to the coreclr static archives, but avoids relying on the alias across wasi-sdk versions and matches the surrounding wasi build. - Filter the runtime-pack framework assemblies into a separate item list once, then run a single Copy on the remainder, instead of an item-metadata Condition on the Copy task (which forced per-file task batching). Relink still builds and runs the full System.Runtime.Tests suite (9930 cases) under wasmtime. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 6d905de1dacdfa..ce25992f34611d 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -68,16 +68,18 @@ Text="System.Private.CoreLib.dll not found in the runtime pack native dir: $(MicrosoftNetCoreAppRuntimePackRidNativeDir)" /> <_WasiCoreCLRCoreLib Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)System.Private.CoreLib.dll" /> - + <_WasiCoreCLRFrameworkFiles Include="$(MicrosoftNetCoreAppRuntimePackRidLibTfmDir)\*.dll" /> + <_WasiCoreCLRFrameworkToCopy Include="@(_WasiCoreCLRFrameworkFiles)" + Condition="!Exists('$(WasmAppDir)managed\%(_WasiCoreCLRFrameworkFiles.FileName)%(_WasiCoreCLRFrameworkFiles.Extension)')" /> - - @@ -173,7 +175,7 @@ force-included coreclr_compat.h supplies the prerequisite types/macros the in-tree PCH would otherwise provide. --> - <_WasiRelinkCompileFlags Include="--target=wasm32-wasip2" /> + <_WasiRelinkCompileFlags Include="--target=wasm32-unknown-wasip2" /> <_WasiRelinkCompileFlags Include="--sysroot="$(_WasiSysroot.Replace('\','/'))"" /> <_WasiRelinkCompileFlags Include="$(_WasiRelinkOptFlag)" /> <_WasiRelinkCompileFlags Include="-DNDEBUG" Condition="'$(Configuration)' != 'Debug'" /> @@ -280,7 +282,7 @@ - <_WasiRelinkLinkFlags Include="--target=wasm32-wasip2" /> + <_WasiRelinkLinkFlags Include="--target=wasm32-unknown-wasip2" /> <_WasiRelinkLinkFlags Include="$(_WasiRelinkOptFlag)" /> <_WasiRelinkLinkFlags Include="-DNDEBUG" Condition="'$(Configuration)' != 'Debug'" /> <_WasiRelinkLinkFlags Include="-Wl,--gc-sections" /> From 6166d4d3993cfe1e2fba01a3542db1a3e9559d3a Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 21:44:09 -0500 Subject: [PATCH 15/22] [wasm] Add WarnOnUnresolvedPInvokeModules flag to the CoreCLR generator WASM0066 (added in #130051) warns for any P/Invoke to a module that isn't statically linked / ignored / [WasmImportLinkage] / "*" / QCall, then skips it (by design: throw-if-called). Untrimmed library-test bundles pull in cross-platform test-support assemblies whose foreign-platform P/Invokes (kernel32, Security.framework, libsecret, OpenSsl, ...) are all unresolved on wasm, so the generator emits hundreds of WASM0066 for a by-design skip and CI's warn-as-error fails the build. Mono uses a different generator; browser-CoreCLR runs only curated Wasm.Build.Tests today - so the wasi library-test leg is the first to feed the generator a real untrimmed closure. Add WarnOnUnresolvedPInvokeModules (default true, preserving today's behavior for hand-authored apps where an unresolved P/Invoke is likely a bug). When false, the unresolved-module case logs a low-importance message instead of WASM0066. WasiApp.CoreCLR.targets sets it false for the library-test relink; browser's CoreCLR targets should adopt the same knob when its library-test leg runs untrimmed closures. Fixes the LibraryTestsCoreCLR_WASI leg build (validated under /p:MSBuildTreatWarningsAsErrors=true; suite still runs 9930 cases). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 9 ++++++++- .../coreclr/ManagedToNativeGenerator.cs | 9 ++++++++- .../coreclr/PInvokeTableGenerator.cs | 14 ++++++++++++-- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index ce25992f34611d..64cbe5ec713cab 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -147,7 +147,13 @@ - + <_WasiManagedAssemblies Include="$(WasmAppDir)managed\*.dll" /> @@ -166,6 +172,7 @@ InterpToNativeOutputPath="$(_WasiInterpToNativeTablePath)" CacheFilePath="$(_WasiM2NCachePath)" TargetOS="wasi" + WarnOnUnresolvedPInvokeModules="false" IsLibraryMode="false"> diff --git a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs index 44053680af94c4..f033d8b39ca3ed 100644 --- a/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/ManagedToNativeGenerator.cs @@ -35,6 +35,13 @@ public class ManagedToNativeGenerator : Task public bool IsLibraryMode { get; set; } + // When true (default), a P/Invoke to a module that isn't statically linked, ignored, + // [WasmImportLinkage], "*" or QCall produces a WASM0066 warning. Consumers that scan + // untrimmed closures full of cross-platform interop (e.g. library-test bundles) set this + // false so the expected "unresolved module, skip and throw-if-called" case is logged as a + // message instead of a build-breaking (under warn-as-error) warning. + public bool WarnOnUnresolvedPInvokeModules { get; set; } = true; + public string TargetOS { get; set; } = "browser"; private static readonly string[] s_knownTargetOSes = new[] { "browser", "wasi" }; @@ -86,7 +93,7 @@ private void ExecuteInternal(LogAdapter log) { Dictionary _symbolNameFixups = new(); List managedAssemblies = FilterOutUnmanagedBinaries(Assemblies); - var pinvoke = new PInvokeTableGenerator(FixupSymbolName, log, IsLibraryMode, TargetOS); + var pinvoke = new PInvokeTableGenerator(FixupSymbolName, log, IsLibraryMode, TargetOS, WarnOnUnresolvedPInvokeModules); var internalCallCollector = new InternalCallSignatureCollector(log); var resolver = new PathAssemblyResolver(managedAssemblies); diff --git a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs index 7542c7ddb55557..a3847d3060f253 100644 --- a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs @@ -25,13 +25,15 @@ internal sealed class PInvokeTableGenerator private readonly List callbacks = new(); private readonly PInvokeCollector _pinvokeCollector; private readonly bool _isLibraryMode; + private readonly bool _warnOnUnresolvedModules; - public PInvokeTableGenerator(Func fixupSymbolName, LogAdapter log, bool isLibraryMode, string targetOS) + public PInvokeTableGenerator(Func fixupSymbolName, LogAdapter log, bool isLibraryMode, string targetOS, bool warnOnUnresolvedModules = true) { Log = log; _fixupSymbolName = fixupSymbolName; _pinvokeCollector = new(log, targetOS); _isLibraryMode = isLibraryMode; + _warnOnUnresolvedModules = warnOnUnresolvedModules; } public void ScanAssembly(Assembly asm) @@ -103,7 +105,15 @@ private void EmitPInvokeTable(StreamWriter w, SortedDictionary m } else if (pinvoke.Module != "QCall") { - Log.Warning("WASM0066", $"PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' is not in the list of allowed modules. It is also not a specially treated module."); + // Unresolved module: not statically linked, ignored, [WasmImportLinkage], "*" or QCall. + // By design we skip it and throw at runtime if it is ever called. For hand-authored + // apps this is likely a bug, so warn; consumers scanning untrimmed closures full of + // cross-platform interop (library-test bundles) disable the warning to avoid failing + // the build under warn-as-error for P/Invokes that are never called on wasm. + if (_warnOnUnresolvedModules) + Log.Warning("WASM0066", $"PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' is not in the list of allowed modules. It is also not a specially treated module."); + else + Log.LogMessage(MessageImportance.Low, $"Skipping unresolved PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' (not statically linked on wasm; will throw if called)."); } } From 4e471347412c038f537abb23807e8929a9713043 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 22:05:26 -0500 Subject: [PATCH 16/22] [wasi] Address PR review: compat-header override + condition ICU on invariant - The missing-coreclr_compat.h error suggested CORECLR_VM_WASM_INCLUDE_DIR/ MINIPAL_INCLUDE_DIR, which only affect the -I include dirs, not where the compat header is found. Give the compat header a real override (CORECLR_COMPAT_HEADER, mirroring the include-dir overrides) and reword the error to name it, so the remediation is truthful for out-of-tree/pack builds (#128362). - Only link libSystem.Globalization.Native.a + the ICU static libs when !InvariantGlobalization, matching BrowserWasmApp.CoreCLR.targets and the generator (which ignores/omits the globalization P/Invoke module when invariant). Verified: the invariant default links clean without them and still runs the full suite (9930 cases). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 64cbe5ec713cab..1d5e110afc4712 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -119,11 +119,12 @@ - <_WasiCoreClrCompatHeader>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', '..', 'browser', 'build', 'coreclr_compat.h')) + and minipal. In-tree uses the repo dirs; env vars override each + for out-of-tree. coreclr_compat.h is referenced from the browser build dir rather + than copied (a second copy would drift from the callhelpers ABI); relocating it to + a shared location + out-of-tree staging is tracked by #128362. --> + <_WasiCoreClrCompatHeader Condition="'$(CORECLR_COMPAT_HEADER)' != ''">$([System.IO.Path]::GetFullPath('$(CORECLR_COMPAT_HEADER)')) + <_WasiCoreClrCompatHeader Condition="'$(_WasiCoreClrCompatHeader)' == ''">$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)', '..', '..', 'browser', 'build', 'coreclr_compat.h')) <_WasiVmWasmIncludeDir Condition="'$(CORECLR_VM_WASM_INCLUDE_DIR)' != ''">$([MSBuild]::EnsureTrailingSlash($([System.IO.Path]::GetFullPath('$(CORECLR_VM_WASM_INCLUDE_DIR)')))) <_WasiVmWasmIncludeDir Condition="'$(_WasiVmWasmIncludeDir)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)..', '..', '..', 'coreclr', 'vm', 'wasm')) <_WasiMinipalIncludeRoot Condition="'$(MINIPAL_INCLUDE_DIR)' != ''">$([MSBuild]::NormalizeDirectory($([System.IO.Path]::GetFullPath('$(MINIPAL_INCLUDE_DIR)')), '..')) @@ -143,7 +144,7 @@ + Text="coreclr_compat.h not found at $(_WasiCoreClrCompatHeader). For out-of-tree/pack builds set %24(CORECLR_COMPAT_HEADER) to the staged header (and CORECLR_VM_WASM_INCLUDE_DIR/MINIPAL_INCLUDE_DIR for the include dirs); see #128362." /> @@ -271,10 +272,12 @@ in libcoreclr_static.a gc-strip. The DECODER for GC stackwalks (TGcInfoDecoder) is in libcoreclr_static.a. Mirrors the corerun executable link, which also omits it. --> - <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> - <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> - <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> - <_WasiHostLibsPre Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicudata.a" /> + + <_WasiHostLibsPre Condition="'$(InvariantGlobalization)' != 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libSystem.Globalization.Native.a" /> + <_WasiHostLibsPre Condition="'$(InvariantGlobalization)' != 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicuuc.a" /> + <_WasiHostLibsPre Condition="'$(InvariantGlobalization)' != 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicui18n.a" /> + <_WasiHostLibsPre Condition="'$(InvariantGlobalization)' != 'true'" Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libicudata.a" /> <_WasiHostLibsPost Include="$(MicrosoftNetCoreAppRuntimePackRidNativeDir)libcoreclrminipal.a" /> From b493aa98e4e1a2313350fb5375c669e6d8bc8971 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 23:27:45 -0500 Subject: [PATCH 17/22] [wasi] Load ICU data in the corerun host so CoreCLR-WASI can run non-invariant The static ICU shim needs the host to preload icudt.dat before managed globalization initializes - GlobalizationNative_LoadICU() (the no-path entry the managed side calls on wasi) returns 0 unless the data was already set and falls back to invariant. Browser does this from its JS host (wasm_load_icu_data); the wasi corerun host had no equivalent, so ICU never worked on CoreCLR-WASI and everything ran invariant. corerun now calls GlobalizationNative_LoadICUData(/icudt.dat) after coreclr_initialize. The symbol is declared weak: it is only linked by the non-invariant per-app relink (which links libSystem.Globalization.Native.a + ICU), so an invariant relink leaves it null and the preload is skipped. WasiApp.CoreCLR.targets stages icudt.dat into managed/ (next to the assemblies) when not invariant. Validated: a non-invariant System.Runtime.Tests bundle now loads ICU and runs with zero CultureNotFoundException failures (was 36+); remaining failures are genuine wasm/interp gaps (no-JIT RuntimeFeature, exception stack line), triaged separately. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- src/coreclr/hosts/corerun/corerun.cpp | 23 +++++++++++++++++++++ src/mono/wasi/build/WasiApp.CoreCLR.targets | 6 ++++++ 2 files changed, 29 insertions(+) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 3ab04343b8a052..21eff89b182ac6 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -395,6 +395,13 @@ static bool HOST_CONTRACT_CALLTYPE external_assembly_probe( extern "C" int corerun_shutdown(int exit_code); static pal::mod_t coreclr_mod; +#ifdef TARGET_WASI +// Provided by libSystem.Globalization.Native.a when globalization is linked (the non-invariant +// per-app relink). Declared weak so an invariant relink (which omits that archive) leaves the +// reference null and the host ICU preload in run() is skipped. +extern "C" __attribute__((weak)) int32_t GlobalizationNative_LoadICUData(const char* path); +#endif + static int run(const configuration& config) { platform_specific_actions actions; @@ -619,6 +626,22 @@ static int run(const configuration& config) coreclr_set_error_writer_func(nullptr); } +#ifdef TARGET_WASI + // The static ICU shim requires the host to preload icudt.dat before managed globalization + // initializes: GlobalizationNative_LoadICU() (the no-path entry the managed side calls on + // wasi) returns 0 unless the data was already set, and falls back to invariant. This mirrors + // the browser JS host calling wasm_load_icu_data. GlobalizationNative_LoadICUData is only + // linked when globalization is enabled (the non-invariant per-app relink), so the file-scope + // declaration is weak - when globalization is not linked the reference is null and this is + // skipped (invariant). A missing icudt.dat also falls back to invariant (the call just fails). + if (GlobalizationNative_LoadICUData != nullptr) + { + pal::string_utf8_t icu_data_path_utf8 = app_path_utf8; + icu_data_path_utf8.append("icudt.dat"); + GlobalizationNative_LoadICUData(icu_data_path_utf8.c_str()); + } +#endif + int exit_code; { actions.before_execute_assembly(config.entry_assembly_fullpath); diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 1d5e110afc4712..b5fd9fa580280a 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -81,6 +81,12 @@ + + From 3411698086f665ea460ba134a19d479eb6c57420 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 14 Jul 2026 23:33:14 -0500 Subject: [PATCH 18/22] [wasi] Run the CoreCLR-WASI library-test leg with full ICU (non-invariant) Now that the relinked corerun host preloads icudt.dat, flip the leg's default from invariant to full ICU globalization (both the app-build default in WasiApp.InTree.props and the run default in tests.wasi.targets). This eliminates the CultureNotFoundException failures (was 36+ under invariant) and gives real globalization coverage; the remaining failures are genuine wasm/interpreter gaps (no-JIT RuntimeFeature/JitInfo, exception stack line) to be triaged separately. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- eng/testing/tests.wasi.targets | 6 +++--- src/mono/wasi/build/WasiApp.InTree.props | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index ebffc594363b34..e960f0a1137129 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -22,10 +22,10 @@ + library/test reverse P/Invoke thunks are covered), and the relinked host preloads + icudt.dat, so the leg runs with full ICU globalization. --> - true + false diff --git a/src/mono/wasi/build/WasiApp.InTree.props b/src/mono/wasi/build/WasiApp.InTree.props index c9da92ff307c4d..a6f5389e84602e 100644 --- a/src/mono/wasi/build/WasiApp.InTree.props +++ b/src/mono/wasi/build/WasiApp.InTree.props @@ -8,10 +8,9 @@ WasiApp.CoreCLR.targets). --> false - - true + + false true From 88d40d8953cf49a8290c4ef7233d11b3d3dfe809 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Wed, 15 Jul 2026 11:18:03 -0500 Subject: [PATCH 19/22] [wasi] ActiveIssue the 3 CoreCLR-WASI interp test gaps in System.Runtime.Tests With ICU working the leg's only failures are 3 genuine wasm-interpreter behavior differences (CI: Tests run 72926, Passed 72812, Failed 3, Skipped 111): - RuntimeFeatureTests.DynamicCode_Jit: the interpreter doesn't JIT-compile, so IsDynamicCodeCompiled is false. Extend the existing browser-CoreCLR ActiveIssue (#123011) from IsBrowser to IsWasm so it also covers wasi. - ExceptionTests.ThrowStatementDoesNotResetExceptionStackLine{Same,Other}Method: the interpreter reports different rethrow stack-line info. These are [ConditionalFact(HasAssemblyFiles)] - browser skips (no assembly files) but wasi has real .dll's so they run and fail. ActiveIssue for IsWasi+IsCoreCLR (#130796). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ea71f7e7-5477-4e58-bf1a-ca27d4b23d74 --- .../tests/System.Runtime.Tests/System/ExceptionTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs index 843813a14997a4..879dfff4cd8d23 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ExceptionTests.cs @@ -110,6 +110,7 @@ public static void Exception_TargetSite_Rethrow() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.HasAssemblyFiles))] [ActiveIssue("https://github.com/mono/mono/issues/15140", TestRuntimes.Mono)] [ActiveIssue("https://github.com/dotnet/runtime/issues/124344", typeof(PlatformDetection), nameof(PlatformDetection.IsAppleMobile), nameof(PlatformDetection.IsCoreCLR))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/130796", typeof(PlatformDetection), nameof(PlatformDetection.IsWasi), nameof(PlatformDetection.IsCoreCLR))] public static void ThrowStatementDoesNotResetExceptionStackLineSameMethod() { (string, string, int) rethrownExceptionStackFrame = (null, null, 0); @@ -141,6 +142,7 @@ private static (string, string, int) ThrowAndRethrowSameMethod(out (string, stri // [ActiveIssue(https://github.com/dotnet/runtime/issues/1871)] can't use ActiveIssue for archs [ActiveIssue("https://github.com/mono/mono/issues/15141", TestRuntimes.Mono)] [ActiveIssue("https://github.com/dotnet/runtime/issues/124344", typeof(PlatformDetection), nameof(PlatformDetection.IsAppleMobile), nameof(PlatformDetection.IsX64Process), nameof(PlatformDetection.IsCoreCLR))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/130796", typeof(PlatformDetection), nameof(PlatformDetection.IsWasi), nameof(PlatformDetection.IsCoreCLR))] public static void ThrowStatementDoesNotResetExceptionStackLineOtherMethod() { (string, string, int) rethrownExceptionStackFrame = (null, null, 0); From c3e9eb08f3e48678a2950af3b100c7f88d7bf6fb Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 16 Jul 2026 16:43:35 -0500 Subject: [PATCH 20/22] [wasi] Consume the shipping wasihost corehost (#130816) in the library-test leg Adapt the CoreCLR-WASI library-test leg to build off the wasihost corehost PR (#130816). Both PRs produced libWasiHost.a in the runtime pack native dir; #130816 supplies it from a proper, statically-linked corehost (src/native/corehost/wasihost), so this leg no longer needs to manufacture its own host from corerun. - corerun/CMakeLists.txt: drop the WasiHost-Static target (libWasiHost.a now comes from the wasihost corehost). - corerun.cpp: revert the wasi ICU preload + weak GlobalizationNative_LoadICUData decl; wasihost.cpp loads icudt.dat from CORE_ROOT. - Directory.Build.props: drop the duplicate libWasiHost.a platform-manifest entry (#130816 adds it in the wasi-specific section). - WasiApp.CoreCLR.targets: the per-app link already links the pack's libWasiHost.a; remove the dead WasiCoreCLRRelinkCoreRun opt-out and the _WasiCopyPrebuiltCoreRun fallback (there is no prebuilt corerun executable in the wasi CoreCLR runtime pack), and update comments to describe linking the shipping wasihost archive. - Refresh corerun/relink wording in the WASI app-builder and test/CI wiring comments to reflect the wasihost host. The per-app native link (ManagedToNativeGenerator -> compile callhelpers -> link libWasiHost.a + runtime-pack static libs + app callhelper .o via wasm-component-ld) is unchanged and matches the link #130816 validated. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f00a53c8-f7ba-4417-8ea6-30d0a1507a81 --- .../wasi-wasm-coreclr-library-tests.yml | 5 +- eng/testing/tests.wasi.targets | 18 +++---- src/coreclr/hosts/corerun/CMakeLists.txt | 19 ------- src/coreclr/hosts/corerun/corerun.cpp | 23 -------- .../Directory.Build.props | 2 - src/libraries/sendtohelix-wasi.targets | 2 +- src/mono/wasi/build/WasiApp.CoreCLR.targets | 54 ++++++++----------- src/mono/wasi/build/WasiApp.InTree.props | 9 ++-- src/mono/wasi/build/WasiApp.InTree.targets | 2 +- 9 files changed, 40 insertions(+), 94 deletions(-) diff --git a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml index 05c425aa861cc6..dff30c87bbfa31 100644 --- a/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml +++ b/eng/pipelines/common/templates/wasi-wasm-coreclr-library-tests.yml @@ -19,8 +19,9 @@ jobs: # See https://github.com/dotnet/runtime/issues/130129. # # Bring-up leg: smoke-only and non-gating (rolling alwaysRun). WasiApp.CoreCLR.targets -# performs a per-app native relink so a test's own [UnmanagedCallersOnly] reverse thunks -# are generated + linked into corerun; with that relink + the nested-type reverse-thunk +# performs a per-app native link of the wasihost corehost so a test's own +# [UnmanagedCallersOnly] reverse thunks are generated + linked into the host; with that link +# + the nested-type reverse-thunk # key fix (also filed standalone as dotnet/runtime#130740; the remaining enclosing-type # collision limitation is tracked by #130739) the interpreter discovers and runs the suite # (validated locally: 9930 System.Runtime.Tests cases). #130634 (cold portable-entry-point diff --git a/eng/testing/tests.wasi.targets b/eng/testing/tests.wasi.targets index e960f0a1137129..9a591156932b1f 100644 --- a/eng/testing/tests.wasi.targets +++ b/eng/testing/tests.wasi.targets @@ -21,9 +21,9 @@ for WBT, and debugger tests --> - + false @@ -31,9 +31,9 @@ <_AppArgs Condition="'$(WasmSingleFileBundle)' == 'true'">$([System.IO.Path]::GetFileNameWithoutExtension('$(WasmMainAssemblyFileName)')).wasm <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' != 'CoreCLR'">dotnet.wasm WasmTestRunner <_AppArgs Condition="'$(WasmSingleFileBundle)' != 'true' and '$(RuntimeFlavor)' == 'CoreCLR'">managed/corerun managed/WasmTestRunner.dll @@ -65,9 +65,9 @@ <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=allow-ip-name-lookup <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--wasi --engine-arg=hostcall-fuel=4294967295 <_XHarnessArgs >$(_XHarnessArgs) --engine-arg=--env --engine-arg=DOTNET_WASI_PRINT_EXIT_CODE=1 - + <_XHarnessArgs Condition="'$(RuntimeFlavor)' == 'CoreCLR'">$(_XHarnessArgs) --engine-arg=-W --engine-arg=exceptions=y <_XHarnessArgs Condition="'$(RuntimeFlavor)' == 'CoreCLR'">$(_XHarnessArgs) --engine-arg=--env --engine-arg=CORE_ROOT=/managed <_XHarnessArgs Condition="'$(WasmXHarnessArgsCli)' != ''" >$(_XHarnessArgs) $(WasmXHarnessArgsCli) diff --git a/src/coreclr/hosts/corerun/CMakeLists.txt b/src/coreclr/hosts/corerun/CMakeLists.txt index 3adb07476cebd3..a4f9237fa7c95a 100644 --- a/src/coreclr/hosts/corerun/CMakeLists.txt +++ b/src/coreclr/hosts/corerun/CMakeLists.txt @@ -142,25 +142,6 @@ endif() install_clr(TARGETS corerun DESTINATIONS . COMPONENT hosts) -# WASI: also build the host as a STATIC archive (libWasiHost.a) so a per-app relink can -# re-link corerun with app-generated callhelpers (replacing the baked coreclr_gen_static), -# mirroring the browser BrowserHost-Static / libBrowserHost.a pattern used by -# BrowserWasmApp.CoreCLR.targets. The runtime static libraries (coreclr_static, System.Native, -# ...) are NOT linked here; they are supplied at relink time from the runtime pack. -# See https://github.com/dotnet/runtime/issues/130129. -if (CLR_CMAKE_TARGET_WASI) - add_library(WasiHost-Static STATIC - corerun.cpp - dotenv.cpp - ./wasm/pinvoke_override.cpp - native.rc - ) - set_target_properties(WasiHost-Static PROPERTIES OUTPUT_NAME WasiHost CLEAN_DIRECT_OUTPUT 1) - target_include_directories(WasiHost-Static PRIVATE ./wasm/) - target_link_libraries(WasiHost-Static PRIVATE minipal) - install_clr(TARGETS WasiHost-Static DESTINATIONS sharedFramework COMPONENT runtime) -endif() - # If there's a dynamic ASAN runtime, then install it in the directory where we put our executable. if (NOT "${ASAN_RUNTIME}" STREQUAL "") install(FILES ${ASAN_RUNTIME} DESTINATION .) diff --git a/src/coreclr/hosts/corerun/corerun.cpp b/src/coreclr/hosts/corerun/corerun.cpp index 21eff89b182ac6..3ab04343b8a052 100644 --- a/src/coreclr/hosts/corerun/corerun.cpp +++ b/src/coreclr/hosts/corerun/corerun.cpp @@ -395,13 +395,6 @@ static bool HOST_CONTRACT_CALLTYPE external_assembly_probe( extern "C" int corerun_shutdown(int exit_code); static pal::mod_t coreclr_mod; -#ifdef TARGET_WASI -// Provided by libSystem.Globalization.Native.a when globalization is linked (the non-invariant -// per-app relink). Declared weak so an invariant relink (which omits that archive) leaves the -// reference null and the host ICU preload in run() is skipped. -extern "C" __attribute__((weak)) int32_t GlobalizationNative_LoadICUData(const char* path); -#endif - static int run(const configuration& config) { platform_specific_actions actions; @@ -626,22 +619,6 @@ static int run(const configuration& config) coreclr_set_error_writer_func(nullptr); } -#ifdef TARGET_WASI - // The static ICU shim requires the host to preload icudt.dat before managed globalization - // initializes: GlobalizationNative_LoadICU() (the no-path entry the managed side calls on - // wasi) returns 0 unless the data was already set, and falls back to invariant. This mirrors - // the browser JS host calling wasm_load_icu_data. GlobalizationNative_LoadICUData is only - // linked when globalization is enabled (the non-invariant per-app relink), so the file-scope - // declaration is weak - when globalization is not linked the reference is null and this is - // skipped (invariant). A missing icudt.dat also falls back to invariant (the call just fails). - if (GlobalizationNative_LoadICUData != nullptr) - { - pal::string_utf8_t icu_data_path_utf8 = app_path_utf8; - icu_data_path_utf8.append("icudt.dat"); - GlobalizationNative_LoadICUData(icu_data_path_utf8.c_str()); - } -#endif - int exit_code; { actions.before_execute_assembly(config.entry_assembly_fullpath); diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index ccf3ca0613d2ec..3720e00b56f0c7 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -271,8 +271,6 @@ - - diff --git a/src/libraries/sendtohelix-wasi.targets b/src/libraries/sendtohelix-wasi.targets index daaef76325e4d7..2da43e62f9cc6f 100644 --- a/src/libraries/sendtohelix-wasi.targets +++ b/src/libraries/sendtohelix-wasi.targets @@ -130,7 +130,7 @@ diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index b5fd9fa580280a..3a42d2a9fcc0a4 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -3,11 +3,12 @@ CoreCLR-WASI app-builder targets. Sibling of Mono's WasiApp.targets, mirroring the browser split (BrowserWasmApp.targets vs BrowserWasmApp.CoreCLR.targets). - Performs a per-app native relink of the corerun host so reverse P/Invoke thunks for the - app/test [UnmanagedCallersOnly] methods are covered (the baked libcoreclr_gen_static.a + Performs a per-app native link of the shipping wasihost corehost (libWasiHost.a, from + src/native/corehost/wasihost) so reverse P/Invoke thunks for the app/test + [UnmanagedCallersOnly] methods are covered (the baked libcoreclr_gen_static.a only covers framework top-level UCO callbacks). Runs ManagedToNativeGenerator (TargetOS=wasi) over the bundle, compiles the generated callhelpers with the wasi-sdk - clang, and relinks corerun from the runtime-pack static archives + the app callhelper .o + clang, and links libWasiHost.a from the runtime-pack static archives + the app callhelper .o (replacing libcoreclr_gen_static.a) via wasm-component-ld. The wasi:http import that System.Net.* pulls in is declared via the component-type link flag (WasiHttpWorld_component_type.wit, as Mono does); wasmtime gets -S http from @@ -28,19 +29,16 @@ CoreCLR false - + false false WasmTestRunner.dll - - true + dotnet.wasm. The wasihost corehost (linked into managed/ as 'corerun' below) is placed + next to the framework; icudt.dat is bundled only when not invariant. --> @@ -59,8 +57,8 @@ + System.Private.CoreLib.dll (pack native dir) into managed/, next to the wasihost host + (linked below), which finds them via CORE_ROOT. See https://github.com/dotnet/runtime/issues/130129. --> @@ -81,34 +79,24 @@ - + - - - - - - - + + Condition="'$(WasmGenerateAppBundle)' == 'true' and '$(WasmSingleFileBundle)' != 'true'"> + Text="$(_ToolchainMissingErrorMessage) The wasi-sdk is required to link the wasihost corehost for the CoreCLR-WASI test host." /> <_WasiRelinkObjDir>$([MSBuild]::NormalizeDirectory('$(_WasmIntermediateOutputPath)', 'corerun-relink')) @@ -256,10 +244,10 @@ - + false - + false diff --git a/src/mono/wasi/build/WasiApp.InTree.targets b/src/mono/wasi/build/WasiApp.InTree.targets index daa44aa7ab79c7..03c856d16ad71d 100644 --- a/src/mono/wasi/build/WasiApp.InTree.targets +++ b/src/mono/wasi/build/WasiApp.InTree.targets @@ -9,7 +9,7 @@ + per-app via wasi-sdk; CoreCLR links the wasihost corehost per-app (WasiApp.CoreCLR.targets). --> From 67c7ec50329e720407ce2342d799f113525ebf3f Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Fri, 17 Jul 2026 12:02:43 -0500 Subject: [PATCH 21/22] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs index a3847d3060f253..13d3a151e47874 100644 --- a/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs +++ b/src/tasks/WasmAppBuilder/coreclr/PInvokeTableGenerator.cs @@ -112,8 +112,8 @@ private void EmitPInvokeTable(StreamWriter w, SortedDictionary m // the build under warn-as-error for P/Invokes that are never called on wasm. if (_warnOnUnresolvedModules) Log.Warning("WASM0066", $"PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' is not in the list of allowed modules. It is also not a specially treated module."); - else - Log.LogMessage(MessageImportance.Low, $"Skipping unresolved PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' (not statically linked on wasm; will throw if called)."); + else if (ignoredModules.Add(pinvoke.Module)) + Log.LogMessage(MessageImportance.Low, $"Skipping unresolved PInvoke module '{pinvoke.Module}' for method '{pinvoke.Method.DeclaringType}::{pinvoke.Method.Name}' (not statically linked on wasm; will throw if called)." ); } } From 400271c150b54d691b57f581553b13f185419ffc Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 20 Jul 2026 14:20:45 -0500 Subject: [PATCH 22/22] [wasi] Don't default WasmMainAssemblyFileName in the CoreCLR app-builder targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WasiApp.CoreCLR.targets hardcoded WasmMainAssemblyFileName=WasmTestRunner.dll, which is a test-runner default in a general app-builder targets file — wrong for a normal (non-test) app. The test path already sets it in eng/testing/tests.wasi.targets, and the browser sibling (BrowserWasmApp.CoreCLR.targets) does not set it either. Remove the default here and let test-specific files supply it. Addresses PR review (radekdoulik). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f00a53c8-f7ba-4417-8ea6-30d0a1507a81 --- src/mono/wasi/build/WasiApp.CoreCLR.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mono/wasi/build/WasiApp.CoreCLR.targets b/src/mono/wasi/build/WasiApp.CoreCLR.targets index 3a42d2a9fcc0a4..9415a5ab7f9ca4 100644 --- a/src/mono/wasi/build/WasiApp.CoreCLR.targets +++ b/src/mono/wasi/build/WasiApp.CoreCLR.targets @@ -33,7 +33,6 @@ wasihost corehost (libWasiHost.a) per-app through its own targets below. --> false false - WasmTestRunner.dll