Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +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, but a basic "hello world" works.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you willing to be listed as target maintainer for this?
I don't think I want to re-land this without a target maintainer.

- 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
Expand Down
1 change: 1 addition & 0 deletions ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +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 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
;;
Expand Down
18 changes: 17 additions & 1 deletion src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,7 +1243,23 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
// foreign function
// Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
let args = ecx.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);

let link_name = if ecx.tcx.sess.target.is_like_wasm
&& let Some(module) =
ecx.tcx.wasm_import_module_map(instance.def_id().krate).get(&instance.def_id())
{
// Adapted from https://github.com/rust-lang/rust/blob/90b65889799733f21ebdf59d96411aa531c5900a/compiler/rustc_codegen_llvm/src/attributes.rs#L549-L562
let codegen_fn_attrs = ecx.tcx.codegen_instance_attrs(instance.def);
let name = codegen_fn_attrs
.symbol_name
.unwrap_or_else(|| ecx.tcx.item_name(instance.def_id()));
// $$$ is unlikely to occur in either the import module name or item name, so use it
// as a separator here. It will be split again in emulate_foreign_item_inner for wasi.
Symbol::intern(&format!("{}$$${}", module, name))
} else {
Symbol::intern(ecx.tcx.symbol_name(instance).name)
};

return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
}

Expand Down
2 changes: 1 addition & 1 deletion src/shims/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl<'tcx> EnvVars<'tcx> {
} else if ecx.tcx.sess.target.os == "windows" {
EnvVars::Windows(WindowsEnvVars::new(ecx, env_vars)?)
} else {
// For "none" targets (i.e., without an OS).
// Used e.g. for wasi
EnvVars::Uninit
};
ecx.machine.env_vars = env_vars;
Expand Down
5 changes: 5 additions & 0 deletions src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_ref();
match this.tcx.sess.target.os.as_ref() {
os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
"wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
"windows" => shims::windows::foreign_items::is_dyn_sym(name),
_ => false,
}
Expand Down Expand Up @@ -845,6 +846,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"wasi" =>
shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
),
"windows" =>
shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
this, link_name, abi, args, dest,
Expand Down
1 change: 1 addition & 0 deletions src/shims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod math;
#[cfg(all(unix, feature = "native-lib"))]
mod native_lib;
mod unix;
mod wasi;
mod windows;
mod x86;

Expand Down
1 change: 1 addition & 0 deletions src/shims/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ impl<'tcx> TlsDtorsState<'tcx> {
}
_ => {
// No TLS dtor support.
// FIXME: should we do something on wasi?
break 'new_state Done;
}
}
Expand Down
124 changes: 124 additions & 0 deletions src/shims/wasi/foreign_items.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use rustc_abi::CanonAbi;
use rustc_middle::ty::Ty;
use rustc_span::Symbol;
use rustc_target::callconv::FnAbi;

use crate::shims::alloc::EvalContextExt as _;
use crate::*;

pub fn is_dyn_sym(_name: &str) -> bool {
false
}

impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn emulate_foreign_item_inner(
&mut self,
link_name: Symbol,
abi: &FnAbi<'tcx, Ty<'tcx>>,
args: &[OpTy<'tcx>],
dest: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, EmulateItemResult> {
let this = self.eval_context_mut();

let (interface, name) = if let Some((module, name)) = link_name.as_str().split_once("$$$") {
// According to the component model, the version should be matched as semver, but for
// simplicity we strip the version entirely for now. Once we support wasm-wasip3 it may
// become actually important to match on the version, but for now it shouldn't matter.
let (module, _version) = module
.split_once('@')
.ok_or_else(|| err_unsup_format!("module name {module} must contain a version"))?;
(Some(module), name)
} else {
// This item is provided by wasi-libc, not imported from the wasi runtime
(None, link_name.as_str())
};

match (interface, name) {
// Allocation
(None, "posix_memalign") => {
let [memptr, align, size] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let result = this.posix_memalign(memptr, align, size)?;
this.write_scalar(result, dest)?;
}
(None, "aligned_alloc") => {
let [align, size] =
this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
let res = this.aligned_alloc(align, size)?;
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.
(Some("wasi:cli/stdout"), "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
}
(Some("wasi:cli/stderr"), "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
}
(Some("wasi:io/streams"), "[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.
}
(Some("wasi:io/streams"), "[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)
}
}
1 change: 1 addition & 0 deletions src/shims/wasi/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod foreign_items;
Loading