From eb49cb1673ffee681a6a3a02fbed70ff66b0e178 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 16:30:41 +0200 Subject: [PATCH 1/2] move _Unwind_RaiseException out of the frame_in_std section --- src/shims/windows/foreign_items.rs | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/shims/windows/foreign_items.rs b/src/shims/windows/foreign_items.rs index 7b13f1d908..c3ca01bbf3 100644 --- a/src/shims/windows/foreign_items.rs +++ b/src/shims/windows/foreign_items.rs @@ -820,6 +820,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(length.strict_sub(1), dest)?; } + "_Unwind_RaiseException" => { + // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. + // It was originally specified as part of the Itanium C++ ABI: + // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. + // MinGW implements _Unwind_RaiseException on top of SEH exceptions. + if this.tcx.sess.target.env != "gnu" { + throw_unsup_format!( + "`_Unwind_RaiseException` is not supported on non-MinGW Windows", + ); + } + // This function looks and behaves excatly like miri_start_unwind. + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return interp_ok(EmulateItemResult::NeedsUnwind); + } + // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { @@ -880,22 +896,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } - "_Unwind_RaiseException" => { - // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. - // It was originally specified as part of the Itanium C++ ABI: - // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. - // MinGW implements _Unwind_RaiseException on top of SEH exceptions. - if this.tcx.sess.target.env != "gnu" { - throw_unsup_format!( - "`_Unwind_RaiseException` is not supported on non-MinGW Windows", - ); - } - // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - this.handle_miri_start_unwind(payload)?; - return interp_ok(EmulateItemResult::NeedsUnwind); - } - _ => return interp_ok(EmulateItemResult::NotSupported), } From fc38fef1afcc91b00258c591f7f67aa42629219e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 12 Sep 2025 17:04:59 +0200 Subject: [PATCH 2/2] make a basic hello world work on wasip2 --- README.md | 2 +- ci/ci.sh | 2 +- src/shims/wasi/foreign_items.rs | 68 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 517aa343a6..7094231b90 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,7 @@ degree documented below): - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite. - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works. + - `wasi`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. - For targets on other operating systems, Miri might fail before even reaching the `main` function. However, even for targets that we do support, the degree of support for accessing platform APIs diff --git a/ci/ci.sh b/ci/ci.sh index 6356e33f61..bcc110f648 100755 --- a/ci/ci.sh +++ b/ci/ci.sh @@ -153,7 +153,7 @@ case $HOST_TARGET in BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd - TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC wasm + TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC hello wasm TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std ;; diff --git a/src/shims/wasi/foreign_items.rs b/src/shims/wasi/foreign_items.rs index bfcdbd8130..ffc02dc986 100644 --- a/src/shims/wasi/foreign_items.rs +++ b/src/shims/wasi/foreign_items.rs @@ -35,6 +35,74 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(res, dest)?; } + // Standard input/output + // FIXME: These shims are hacks that just get basic stdout/stderr working. We can't + // constrain them to "std" since std itself uses the wasi crate for this. + "get-stdout" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(1), dest)?; // POSIX FD number for stdout + } + "get-stderr" => { + let [] = + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; + this.write_scalar(Scalar::from_i32(2), dest)?; // POSIX FD number for stderr + } + "[resource-drop]output-stream" => { + let [handle] = + this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?; + let handle = this.read_scalar(handle)?.to_i32()?; + + if !(handle == 1 || handle == 2) { + throw_unsup_format!("wasm output-stream: unsupported handle"); + } + // We don't actually close these FDs, so this is a NOP. + } + "[method]output-stream.blocking-write-and-flush" => { + let [handle, buf, len, ret_area] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, *mut _) -> ()), + link_name, + abi, + args, + )?; + let handle = this.read_scalar(handle)?.to_i32()?; + let buf = this.read_pointer(buf)?; + let len = this.read_target_usize(len)?; + let ret_area = this.read_pointer(ret_area)?; + + if len > 4096 { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: buffer too big" + ); + } + let len = usize::try_from(len).unwrap(); + let Some(fd) = this.machine.fds.get(handle) else { + throw_unsup_format!( + "wasm output-stream.blocking-write-and-flush: unsupported handle" + ); + }; + fd.write( + this.machine.communicate(), + buf, + len, + this, + callback!( + @capture<'tcx> { + len: usize, + ret_area: Pointer, + } + |this, result: Result| { + if !matches!(result, Ok(l) if l == len) { + throw_unsup_format!("wasm output-stream.blocking-write-and-flush: returning errors is not supported"); + } + // 0 in the first byte of the ret_area indicates success. + let ret = this.ptr_to_mplace(ret_area, this.machine.layouts.u8); + this.write_null(&ret)?; + interp_ok(()) + }), + )?; + } + _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn)