1use std::io::Write;
15
16use wasmtime::{Caller, Linker, Module, Val};
17
18use crate::host::{Host, guest_slice, guest_slice_mut, memory};
19
20pub const MODULE: &str = "wasi_snapshot_preview1";
21
22const SUCCESS: i32 = 0;
23const EBADF: i32 = 8;
24const EINVAL: i32 = 28;
25const ENOSYS: i32 = 52;
26const ESPIPE: i32 = 70;
27
28#[derive(Debug)]
30pub struct Exit(pub i32);
31
32impl std::fmt::Display for Exit {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(f, "guest called proc_exit({})", self.0)
35 }
36}
37
38impl std::error::Error for Exit {}
39
40#[derive(Clone, Copy)]
43pub struct Splitmix(pub u64);
44
45impl Splitmix {
46 pub fn next_u64(&mut self) -> u64 {
47 self.0 = self.0.wrapping_add(0x9e37_79b9_7f4a_7c15);
48 let mut z = self.0;
49 z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
50 z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
51 z ^ (z >> 31)
52 }
53
54 pub fn fill(&mut self, out: &mut [u8]) {
55 for chunk in out.chunks_mut(8) {
56 let v = self.next_u64().to_le_bytes();
57 chunk.copy_from_slice(&v[..chunk.len()]);
58 }
59 }
60}
61
62fn write_u32(caller: &mut Caller<'_, Host>, ptr: u32, v: u32) -> wasmtime::Result<()> {
63 let mem = memory(caller)?;
64 guest_slice_mut(mem.data_mut(caller), ptr, 4)?.copy_from_slice(&v.to_le_bytes());
65 Ok(())
66}
67
68fn write_u64(caller: &mut Caller<'_, Host>, ptr: u32, v: u64) -> wasmtime::Result<()> {
69 let mem = memory(caller)?;
70 guest_slice_mut(mem.data_mut(caller), ptr, 8)?.copy_from_slice(&v.to_le_bytes());
71 Ok(())
72}
73
74fn clock_ns(host: &Host, id: u32) -> u64 {
77 match host.virtual_time_ms {
78 Some(ms) => (ms * 1e6).round() as u64,
79 None if id == 0 => std::time::SystemTime::now()
80 .duration_since(std::time::UNIX_EPOCH)
81 .map_or(0, |d| d.as_nanos() as u64),
82 None => host.started().elapsed().as_nanos() as u64,
83 }
84}
85
86pub const IMPLEMENTED: [&str; 14] = [
88 "fd_write", "fd_close", "fd_seek", "fd_prestat_get", "fd_fdstat_get", "clock_res_get", "clock_time_get",
89 "random_get", "args_sizes_get", "environ_sizes_get", "args_get", "environ_get", "sched_yield", "proc_exit",
90];
91
92pub fn add_to_linker(linker: &mut Linker<Host>, module: &Module) -> wasmtime::Result<()> {
93 linker.func_wrap(MODULE, "fd_write", |mut caller: Caller<'_, Host>, fd: i32, iovs: u32, iovs_len: u32, nwritten: u32| -> wasmtime::Result<i32> {
94 if fd != 1 && fd != 2 {
95 return Ok(EBADF);
96 }
97 let mem = memory(&caller)?;
98 let data = mem.data(&caller);
99 let list = guest_slice(data, iovs, iovs_len as u64 * 8)?;
100 let mut total = 0u32;
101 let mut err = std::io::stderr().lock();
102 for iov in list.chunks_exact(8) {
103 let ptr = u32::from_le_bytes([iov[0], iov[1], iov[2], iov[3]]);
104 let len = u32::from_le_bytes([iov[4], iov[5], iov[6], iov[7]]);
105 let _ = err.write_all(guest_slice(data, ptr, len as u64)?);
106 total = total.wrapping_add(len);
107 }
108 drop(err);
109 write_u32(&mut caller, nwritten, total)?;
110 Ok(SUCCESS)
111 })?;
112 linker.func_wrap(MODULE, "fd_close", |_: Caller<'_, Host>, _fd: i32| -> i32 { SUCCESS })?;
113 linker.func_wrap(MODULE, "fd_seek", |_: Caller<'_, Host>, _fd: i32, _off: i64, _whence: i32, _out: u32| -> i32 { ESPIPE })?;
114 linker.func_wrap(MODULE, "fd_prestat_get", |_: Caller<'_, Host>, _fd: i32, _out: u32| -> i32 { EBADF })?;
115 linker.func_wrap(MODULE, "fd_fdstat_get", |mut caller: Caller<'_, Host>, fd: i32, out: u32| -> wasmtime::Result<i32> {
116 if !(0..=2).contains(&fd) {
117 return Ok(EBADF);
118 }
119 let mem = memory(&caller)?;
120 let stat = guest_slice_mut(mem.data_mut(&mut caller), out, 24)?;
121 stat.fill(0);
122 stat[0] = 2; Ok(SUCCESS)
124 })?;
125 linker.func_wrap(MODULE, "clock_res_get", |mut caller: Caller<'_, Host>, id: u32, out: u32| -> wasmtime::Result<i32> {
126 if id > 3 {
127 return Ok(EINVAL);
128 }
129 write_u64(&mut caller, out, 1000)?;
130 Ok(SUCCESS)
131 })?;
132 linker.func_wrap(MODULE, "clock_time_get", |mut caller: Caller<'_, Host>, id: u32, _precision: i64, out: u32| -> wasmtime::Result<i32> {
133 if id > 3 {
134 return Ok(EINVAL);
135 }
136 let ns = clock_ns(caller.data(), id);
137 write_u64(&mut caller, out, ns)?;
138 Ok(SUCCESS)
139 })?;
140 linker.func_wrap(MODULE, "random_get", |mut caller: Caller<'_, Host>, ptr: u32, len: u32| -> wasmtime::Result<i32> {
141 let mem = memory(&caller)?;
142 let (data, host) = mem.data_and_store_mut(&mut caller);
143 let buf = guest_slice_mut(data, ptr, len as u64)?;
144 match &mut host.random {
145 Some(rng) => rng.fill(buf),
146 None => getrandom::fill(buf).map_err(|e| wasmtime::format_err!("random_get: {e}"))?,
147 }
148 Ok(SUCCESS)
149 })?;
150 for name in ["args_sizes_get", "environ_sizes_get"] {
151 linker.func_wrap(MODULE, name, |mut caller: Caller<'_, Host>, count: u32, buf_size: u32| -> wasmtime::Result<i32> {
152 write_u32(&mut caller, count, 0)?;
153 write_u32(&mut caller, buf_size, 0)?;
154 Ok(SUCCESS)
155 })?;
156 }
157 linker.func_wrap(MODULE, "args_get", |_: Caller<'_, Host>, _a: u32, _b: u32| -> i32 { SUCCESS })?;
158 linker.func_wrap(MODULE, "environ_get", |_: Caller<'_, Host>, _a: u32, _b: u32| -> i32 { SUCCESS })?;
159 linker.func_wrap(MODULE, "sched_yield", |_: Caller<'_, Host>| -> i32 { SUCCESS })?;
160 linker.func_wrap(MODULE, "proc_exit", |_: Caller<'_, Host>, code: i32| -> wasmtime::Result<()> {
161 Err(wasmtime::Error::new(Exit(code)))
162 })?;
163
164 for import in module.imports().filter(|i| i.module() == MODULE && !IMPLEMENTED.contains(&i.name())) {
166 let Some(ty) = import.ty().func().cloned() else { continue };
167 let returns_errno = ty.results().len() == 1 && ty.results().next().is_some_and(|t| t.is_i32());
168 let name = import.name().to_owned();
169 let what = format!("unsupported WASI function {MODULE}.{name}");
170 linker.func_new(MODULE, &name, ty, move |_, _params, results| {
171 if returns_errno {
172 results[0] = Val::I32(ENOSYS);
173 Ok(())
174 } else {
175 Err(wasmtime::format_err!("{what}"))
176 }
177 })?;
178 }
179 Ok(())
180}