Skip to content

Commit 100d2be

Browse files
authored
fix initialization of tinygo programs (#118)
* fix: ensure Go programs built with tinygo >= 0.35.0 run WASI programs built by tinygo have to be written with a `main` function, even if it's empty. Starting from tinygo >= 0.35.0, the `main` function calls the WASI process exit function, which is handled by wasmtime as an Error. We must check if this error can be converted into a WASI exit error and, if the exit code is 0, we can ignore it. Otherwise the waPC initialization will fail. Signed-off-by: Flavio Castelli <fcastelli@suse.com> * wasmtime-provider: tag patch release - fix: tinygo 0.35.0 programs do not run Signed-off-by: Flavio Castelli <fcastelli@suse.com> --------- Signed-off-by: Flavio Castelli <fcastelli@suse.com>
1 parent 008a2be commit 100d2be

File tree

4 files changed

+54
-16
lines changed

4 files changed

+54
-16
lines changed

crates/wasmtime-provider/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "wasmtime-provider"
3-
version = "2.3.0"
3+
version = "2.3.1"
44
authors = [
55
"Kevin Hoffman <alothien@gmail.com>",
66
"Jarrod Overson <jsoverson@gmail.com>",
@@ -72,6 +72,7 @@ async-trait = { version = "0.1", optional = true }
7272
tokio = { version = "1", optional = true, default-features = false, features = [
7373
"rt",
7474
] }
75+
tracing = "0.1"
7576

7677
[dev-dependencies]
7778
wapc-codec = { path = "../wapc-codec" }

crates/wasmtime-provider/src/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub(crate) type Result<T> = std::result::Result<T, Error>;
1010
pub enum Error {
1111
/// Wasmtime initialization failed
1212
#[error("Initialization failed: {0}")]
13-
InitializationFailed(Box<dyn std::error::Error + Send + Sync>),
13+
InitializationFailed(String),
1414

1515
/// Wasmtime initialization failed
1616
#[error("Initialization failed: {0} init interrupted, execution deadline exceeded")]

crates/wasmtime-provider/src/provider.rs

+26-7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::sync::Arc;
22

33
use log::{error, info};
44
use parking_lot::RwLock;
5+
use tracing::trace;
56
#[cfg(feature = "wasi")]
67
use wapc::WasiParams;
78
use wapc::{wapc_functions, ModuleState, WebAssemblyEngineProvider};
@@ -248,6 +249,7 @@ impl WebAssemblyEngineProvider for WasmtimeEngineProvider {
248249
impl WasmtimeEngineProvider {
249250
fn initialize(&mut self) -> Result<()> {
250251
for starter in wapc_functions::REQUIRED_STARTS.iter() {
252+
trace!(function = starter, "calling init function");
251253
if let Some(deadlines) = &self.epoch_deadlines {
252254
// the deadline counter must be set before invoking the wasm function
253255
self.store.set_epoch_deadline(deadlines.wapc_init);
@@ -265,17 +267,34 @@ impl WasmtimeEngineProvider {
265267
// generic `anyhow::Error` that doesn't allow nice handling of
266268
// errors
267269
let starter_func: TypedFunc<(), ()> = engine_inner.instance.read().get_typed_func(&mut self.store, starter)?;
268-
starter_func.call(&mut self.store, ()).map_err(|err| {
270+
271+
if let Err(err) = starter_func.call(&mut self.store, ()) {
272+
trace!(function = starter, ?err, "handling error returned by init function");
269273
if let Some(trap) = err.downcast_ref::<wasmtime::Trap>() {
270274
if matches!(trap, wasmtime::Trap::Interrupt) {
271-
Error::InitializationFailedTimeout((*starter).to_owned())
272-
} else {
273-
Error::InitializationFailed(err.into())
275+
return Err(Error::InitializationFailedTimeout((*starter).to_owned()));
274276
}
275-
} else {
276-
Error::InitializationFailed(err.into())
277+
return Err(Error::InitializationFailed(err.to_string()));
277278
}
278-
})?;
279+
280+
// WASI programs built by tinygo have to be written with a `main` function, even if it's empty.
281+
// Starting from tinygo >= 0.35.0, the `main` function calls the WASI process exit function,
282+
// which is handled by wasmtime as an Error.
283+
//
284+
// We must check if this error can be converted into a WASI exit
285+
// error and, if the exit code is 0, we can ignore it. Otherwise the waPC initialization
286+
// will fail.
287+
#[cfg(feature = "wasi")]
288+
if let Some(exit_err) = err.downcast_ref::<wasi_common::I32Exit>() {
289+
if exit_err.0 != 0 {
290+
return Err(Error::InitializationFailed(err.to_string()));
291+
}
292+
trace!("ignoring successful exit trap generated by WASI");
293+
continue;
294+
}
295+
296+
return Err(Error::InitializationFailed(err.to_string()));
297+
};
279298
}
280299
}
281300
Ok(())

crates/wasmtime-provider/src/provider_async.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use log::{error, info};
44

55
use async_trait::async_trait;
66
use parking_lot::RwLock;
7+
use tracing::trace;
78
#[cfg(feature = "wasi")]
89
use wapc::WasiParams;
910
use wapc::{wapc_functions, ModuleStateAsync, WebAssemblyEngineProviderAsync};
@@ -322,17 +323,34 @@ impl WasmtimeEngineProviderAsync {
322323
// generic `anyhow::Error` that doesn't allow nice handling of
323324
// errors
324325
let starter_func: TypedFunc<(), ()> = engine_inner.instance.read().get_typed_func(&mut self.store, starter)?;
325-
starter_func.call_async(&mut self.store, ()).await.map_err(|err| {
326+
327+
if let Err(err) = starter_func.call_async(&mut self.store, ()).await {
328+
trace!(function = starter, ?err, "handling error returned by init function");
326329
if let Some(trap) = err.downcast_ref::<wasmtime::Trap>() {
327330
if matches!(trap, wasmtime::Trap::Interrupt) {
328-
Error::InitializationFailedTimeout((*starter).to_owned())
329-
} else {
330-
Error::InitializationFailed(err.into())
331+
return Err(Error::InitializationFailedTimeout((*starter).to_owned()));
331332
}
332-
} else {
333-
Error::InitializationFailed(err.into())
333+
return Err(Error::InitializationFailed(err.to_string()));
334334
}
335-
})?;
335+
336+
// WASI programs built by tinygo have to be written with a `main` function, even if it's empty.
337+
// Starting from tinygo >= 0.35.0, the `main` function calls the WASI process exit function,
338+
// which is handled by wasmtime as an Error.
339+
//
340+
// We must check if this error can be converted into a WASI exit
341+
// error and, if the exit code is 0, we can ignore it. Otherwise the waPC initialization
342+
// will fail.
343+
#[cfg(feature = "wasi")]
344+
if let Some(exit_err) = err.downcast_ref::<wasi_common::I32Exit>() {
345+
if exit_err.0 != 0 {
346+
return Err(Error::InitializationFailed(err.to_string()));
347+
}
348+
trace!("ignoring successful exit trap generated by WASI");
349+
continue;
350+
}
351+
352+
return Err(Error::InitializationFailed(err.to_string()));
353+
};
336354
}
337355
}
338356
Ok(())

0 commit comments

Comments
 (0)