make a basic hello world work on wasip2

This commit is contained in:
Ralf Jung 2025-09-12 17:04:59 +02:00
parent 225e8ff17b
commit 260dd6f7ea
3 changed files with 70 additions and 2 deletions

View file

@ -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

View file

@ -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
;;

View file

@ -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<usize, IoError>| {
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)