diff --git a/crates/guest-rust/build.rs b/crates/guest-rust/build.rs index 0793c250d..b7f0f94c5 100644 --- a/crates/guest-rust/build.rs +++ b/crates/guest-rust/build.rs @@ -1,7 +1,17 @@ use std::env; use std::path::PathBuf; +use std::process::Command; fn main() { + println!("cargo:rustc-check-cfg=cfg(wasi_has_initialize)"); + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + // Rust 1.100 supplies `_initialize` for WASI cdylibs: + // https://github.com/rust-lang/rust/pull/161421 + // Remove the version check once the MSRV reaches 1.100. + if target_os == "wasi" && rustc_minor_version().unwrap_or(0) >= 100 { + println!("cargo:rustc-cfg=wasi_has_initialize"); + } let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or(String::new()); let target_family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap_or(String::new()); let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap_or(String::new()); @@ -26,6 +36,17 @@ fn main() { } } +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = std::str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +} + fn link_lib(name: &str) { let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let mut src = env::current_dir().unwrap(); diff --git a/crates/guest-rust/src/rt/mod.rs b/crates/guest-rust/src/rt/mod.rs index 27e233695..df7141fe9 100644 --- a/crates/guest-rust/src/rt/mod.rs +++ b/crates/guest-rust/src/rt/mod.rs @@ -200,20 +200,24 @@ pub unsafe fn cabi_realloc( /// `cabi_realloc`, which is a *core* export func, but should not execute ctors. #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { - static mut RUN: bool = false; - unsafe { - if !RUN { - // This function is synthesized by `wasm-ld` to run all static - // constructors. wasm-ld will either provide an implementation - // of this symbol, or synthesize a wrapper around each - // exported function to (unconditionally) run ctors. By using - // this function, the linked module is opting into "manually" - // running ctors. - unsafe extern "C" { - fn __wasm_call_ctors(); + #[cfg(not(wasi_has_initialize))] + { + static mut RUN: bool = false; + + unsafe { + if !RUN { + // This function is synthesized by `wasm-ld` to run all static + // constructors. wasm-ld will either provide an implementation + // of this symbol, or synthesize a wrapper around each + // exported function to (unconditionally) run ctors. By using + // this function, the linked module is opting into "manually" + // running ctors. + unsafe extern "C" { + fn __wasm_call_ctors(); + } + __wasm_call_ctors(); + RUN = true; } - __wasm_call_ctors(); - RUN = true; } } } diff --git a/tests/runtime/rust/ctors-run-once/runner.rs b/tests/runtime/rust/ctors-run-once/runner.rs new file mode 100644 index 000000000..3d34844e3 --- /dev/null +++ b/tests/runtime/rust/ctors-run-once/runner.rs @@ -0,0 +1,14 @@ +include!(env!("BINDINGS")); + +struct Component; + +export!(Component); + +impl Guest for Component { + fn run() { + // Startup may have run the constructors at instantiation or at the + // first export call. Neither export call should run them again. + assert_eq!(the::test::i::ctor_runs(), 1); + assert_eq!(the::test::i::ctor_runs(), 1); + } +} diff --git a/tests/runtime/rust/ctors-run-once/test.rs b/tests/runtime/rust/ctors-run-once/test.rs new file mode 100644 index 000000000..b73434cda --- /dev/null +++ b/tests/runtime/rust/ctors-run-once/test.rs @@ -0,0 +1,23 @@ +include!(env!("BINDINGS")); + +use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; + +static CTOR_RUNS: AtomicU32 = AtomicU32::new(0); + +extern "C" fn ctor() { + CTOR_RUNS.fetch_add(1, Relaxed); +} + +#[used] +#[unsafe(link_section = ".init_array")] +static CTOR: extern "C" fn() = ctor; + +struct Test; + +export!(Test); + +impl exports::the::test::i::Guest for Test { + fn ctor_runs() -> u32 { + CTOR_RUNS.load(Relaxed) + } +} diff --git a/tests/runtime/rust/ctors-run-once/test.wit b/tests/runtime/rust/ctors-run-once/test.wit new file mode 100644 index 000000000..aa7ff0d72 --- /dev/null +++ b/tests/runtime/rust/ctors-run-once/test.wit @@ -0,0 +1,14 @@ +package the:test; + +interface i { + ctor-runs: func() -> u32; +} + +world runner { + import i; + export run: func(); +} + +world test { + export i; +}