|
| 1 | +use rustc_abi::CanonAbi; |
| 2 | +use rustc_middle::ty::Ty; |
| 3 | +use rustc_span::Symbol; |
| 4 | +use rustc_target::callconv::FnAbi; |
| 5 | + |
| 6 | +use crate::shims::alloc::EvalContextExt as _; |
| 7 | +use crate::*; |
| 8 | + |
| 9 | +pub fn is_dyn_sym(_name: &str) -> bool { |
| 10 | + false |
| 11 | +} |
| 12 | + |
| 13 | +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} |
| 14 | +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 15 | + fn emulate_foreign_item_inner( |
| 16 | + &mut self, |
| 17 | + link_name: Symbol, |
| 18 | + abi: &FnAbi<'tcx, Ty<'tcx>>, |
| 19 | + args: &[OpTy<'tcx>], |
| 20 | + dest: &MPlaceTy<'tcx>, |
| 21 | + ) -> InterpResult<'tcx, EmulateItemResult> { |
| 22 | + let this = self.eval_context_mut(); |
| 23 | + |
| 24 | + let (interface, name) = if let Some((module, name)) = link_name.as_str().split_once("$$$") { |
| 25 | + // According to the component model, the version should be matched as semver, but for |
| 26 | + // simplicity we strip the version entirely for now. Once we support wasm-wasip3 it may |
| 27 | + // become actually important to match on the version, but for now it shouldn't matter. |
| 28 | + let (module, _version) = module |
| 29 | + .split_once('@') |
| 30 | + .ok_or_else(|| err_unsup_format!("module name {module} must contain a version"))?; |
| 31 | + (Some(module), name) |
| 32 | + } else { |
| 33 | + // This item is provided by wasi-libc, not imported from the wasi runtime |
| 34 | + (None, link_name.as_str()) |
| 35 | + }; |
| 36 | + |
| 37 | + match (interface, name) { |
| 38 | + // Allocation |
| 39 | + (None, "posix_memalign") => { |
| 40 | + let [memptr, align, size] = |
| 41 | + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 42 | + let result = this.posix_memalign(memptr, align, size)?; |
| 43 | + this.write_scalar(result, dest)?; |
| 44 | + } |
| 45 | + (None, "aligned_alloc") => { |
| 46 | + let [align, size] = |
| 47 | + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; |
| 48 | + let res = this.aligned_alloc(align, size)?; |
| 49 | + this.write_pointer(res, dest)?; |
| 50 | + } |
| 51 | + |
| 52 | + // Standard input/output |
| 53 | + // FIXME: These shims are hacks that just get basic stdout/stderr working. We can't |
| 54 | + // constrain them to "std" since std itself uses the wasi crate for this. |
| 55 | + (Some("wasi:cli/stdout"), "get-stdout") => { |
| 56 | + let [] = |
| 57 | + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; |
| 58 | + this.write_scalar(Scalar::from_i32(1), dest)?; // POSIX FD number for stdout |
| 59 | + } |
| 60 | + (Some("wasi:cli/stderr"), "get-stderr") => { |
| 61 | + let [] = |
| 62 | + this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?; |
| 63 | + this.write_scalar(Scalar::from_i32(2), dest)?; // POSIX FD number for stderr |
| 64 | + } |
| 65 | + (Some("wasi:io/streams"), "[resource-drop]output-stream") => { |
| 66 | + let [handle] = |
| 67 | + this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?; |
| 68 | + let handle = this.read_scalar(handle)?.to_i32()?; |
| 69 | + |
| 70 | + if !(handle == 1 || handle == 2) { |
| 71 | + throw_unsup_format!("wasm output-stream: unsupported handle"); |
| 72 | + } |
| 73 | + // We don't actually close these FDs, so this is a NOP. |
| 74 | + } |
| 75 | + (Some("wasi:io/streams"), "[method]output-stream.blocking-write-and-flush") => { |
| 76 | + let [handle, buf, len, ret_area] = this.check_shim_sig( |
| 77 | + shim_sig!(extern "C" fn(i32, *mut _, usize, *mut _) -> ()), |
| 78 | + link_name, |
| 79 | + abi, |
| 80 | + args, |
| 81 | + )?; |
| 82 | + let handle = this.read_scalar(handle)?.to_i32()?; |
| 83 | + let buf = this.read_pointer(buf)?; |
| 84 | + let len = this.read_target_usize(len)?; |
| 85 | + let ret_area = this.read_pointer(ret_area)?; |
| 86 | + |
| 87 | + if len > 4096 { |
| 88 | + throw_unsup_format!( |
| 89 | + "wasm output-stream.blocking-write-and-flush: buffer too big" |
| 90 | + ); |
| 91 | + } |
| 92 | + let len = usize::try_from(len).unwrap(); |
| 93 | + let Some(fd) = this.machine.fds.get(handle) else { |
| 94 | + throw_unsup_format!( |
| 95 | + "wasm output-stream.blocking-write-and-flush: unsupported handle" |
| 96 | + ); |
| 97 | + }; |
| 98 | + fd.write( |
| 99 | + this.machine.communicate(), |
| 100 | + buf, |
| 101 | + len, |
| 102 | + this, |
| 103 | + callback!( |
| 104 | + @capture<'tcx> { |
| 105 | + len: usize, |
| 106 | + ret_area: Pointer, |
| 107 | + } |
| 108 | + |this, result: Result<usize, IoError>| { |
| 109 | + if !matches!(result, Ok(l) if l == len) { |
| 110 | + throw_unsup_format!("wasm output-stream.blocking-write-and-flush: returning errors is not supported"); |
| 111 | + } |
| 112 | + // 0 in the first byte of the ret_area indicates success. |
| 113 | + let ret = this.ptr_to_mplace(ret_area, this.machine.layouts.u8); |
| 114 | + this.write_null(&ret)?; |
| 115 | + interp_ok(()) |
| 116 | + }), |
| 117 | + )?; |
| 118 | + } |
| 119 | + |
| 120 | + _ => return interp_ok(EmulateItemResult::NotSupported), |
| 121 | + } |
| 122 | + interp_ok(EmulateItemResult::NeedsReturn) |
| 123 | + } |
| 124 | +} |
0 commit comments