Skip to content

Commit

Permalink
Replace signals-based-traps with auto-detection
Browse files Browse the repository at this point in the history
This commit refactors the platform support of the `wasmtime` crate
itself to remove the previously added `signals-based-traps` feature in
favor of auto-detecting whether it's there or not. The `build.rs`
script for the `wasmtime` crate will now detect the target platform and
auto-enable this feature as necessary.

The `signals-based-traps` cargo feature is removed and split into two
custom `#[cfg]` directives that the build script sets:

* `has_virtual_memory` - this is used to gate mmap implementations for
  example. This is enabled on `unix || windows` and will be off for
  `no_std` targets for example. This is split out of
  "signals-based-traps" to better handle platforms like iOS which have
  virtual memory but don't execute native code (removing the need for
  native signals).

* `has_native_signals` - gates signal handlers on Unix for example. This
  is disabled on MIRI but otherwise enabled for `unix || windows`. This
  is intended to in the future get disabled for iOS by default for
  example since it's not necessary when using Pulley. This is
  additionally off-by-default for `no_std` platforms.

Two new crate features were added for `no_std` or "custom" platforms to
opt-in to the `wasmtime-platform.h` C APIs for implementing virtual
memory and signals. These are used in the `min-platform` embedding example.

This commit additionally updates some various documentation here and
there to be more up-to-date.
  • Loading branch information
alexcrichton committed Jan 7, 2025
1 parent 5030709 commit 033488a
Show file tree
Hide file tree
Showing 36 changed files with 280 additions and 272 deletions.
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ rustix = { workspace = true, features = ["mm", "param", "process"] }

[dev-dependencies]
# depend again on wasmtime to activate its default features for tests
wasmtime = { workspace = true, features = ['default', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys', 'signals-based-traps'] }
wasmtime = { workspace = true, features = ['default', 'winch', 'pulley', 'all-arch', 'call-hook', 'memory-protection-keys'] }
env_logger = { workspace = true }
log = { workspace = true }
filecheck = { workspace = true }
Expand Down Expand Up @@ -404,7 +404,6 @@ default = [
"addr2line",
"debug-builtins",
"component-model",
"signals-based-traps",
"threads",
"gc",
"gc-drc",
Expand Down Expand Up @@ -463,7 +462,6 @@ threads = ["wasmtime-cli-flags/threads"]
gc = ["wasmtime-cli-flags/gc", "wasmtime/gc"]
gc-drc = ["gc", "wasmtime/gc-drc", "wasmtime-cli-flags/gc-drc"]
gc-null = ["gc", "wasmtime/gc-null", "wasmtime-cli-flags/gc-null"]
signals-based-traps = ["wasmtime/signals-based-traps", "wasmtime-cli-flags/signals-based-traps"]

# CLI subcommands for the `wasmtime` executable. See `wasmtime $cmd --help`
# for more information on each subcommand.
Expand Down
1 change: 0 additions & 1 deletion crates/cli-flags/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ gc-drc = ["gc", "wasmtime/gc-drc"]
gc-null = ["gc", "wasmtime/gc-null"]
threads = ["wasmtime/threads"]
memory-protection-keys = ["wasmtime/memory-protection-keys"]
signals-based-traps = ["wasmtime/signals-based-traps"]
48 changes: 16 additions & 32 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,47 +661,35 @@ impl CommonOptions {
.opts
.memory_reservation
.or(self.opts.static_memory_maximum_size);
match_feature! {
["signals-based-traps" : memory_reservation]
size => config.memory_reservation(size),
_ => err,
if let Some(size) = memory_reservation {
config.memory_reservation(size);
}

match_feature! {
["signals-based-traps" : self.opts.static_memory_forced]
enable => config.memory_may_move(!enable),
_ => err,
if let Some(enable) = self.opts.static_memory_forced {
config.memory_may_move(!enable);
}
match_feature! {
["signals-based-traps" : self.opts.memory_may_move]
enable => config.memory_may_move(enable),
_ => err,
if let Some(enable) = self.opts.memory_may_move {
config.memory_may_move(enable);
}

let memory_guard_size = self
.opts
.static_memory_guard_size
.or(self.opts.dynamic_memory_guard_size)
.or(self.opts.memory_guard_size);
match_feature! {
["signals-based-traps" : memory_guard_size]
size => config.memory_guard_size(size),
_ => err,
if let Some(size) = memory_guard_size {
config.memory_guard_size(size);
}

let mem_for_growth = self
.opts
.memory_reservation_for_growth
.or(self.opts.dynamic_memory_reserved_for_growth);
match_feature! {
["signals-based-traps" : mem_for_growth]
size => config.memory_reservation_for_growth(size),
_ => err,
if let Some(size) = mem_for_growth {
config.memory_reservation_for_growth(size);
}
match_feature! {
["signals-based-traps" : self.opts.guard_before_linear_memory]
enable => config.guard_before_linear_memory(enable),
_ => err,
if let Some(enable) = self.opts.guard_before_linear_memory {
config.guard_before_linear_memory(enable);
}
if let Some(enable) = self.opts.table_lazy_init {
config.table_lazy_init(enable);
Expand All @@ -718,15 +706,11 @@ impl CommonOptions {
if let Some(enable) = self.debug.address_map {
config.generate_address_map(enable);
}
match_feature! {
["signals-based-traps" : self.opts.memory_init_cow]
enable => config.memory_init_cow(enable),
_ => err,
if let Some(enable) = self.opts.memory_init_cow {
config.memory_init_cow(enable);
}
match_feature! {
["signals-based-traps" : self.opts.signals_based_traps]
enable => config.signals_based_traps(enable),
_ => err,
if let Some(enable) = self.opts.signals_based_traps {
config.signals_based_traps(enable);
}
if let Some(enable) = self.codegen.native_unwind_info {
config.native_unwind_info(enable);
Expand Down
2 changes: 1 addition & 1 deletion crates/fuzzing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ target-lexicon = { workspace = true }
tempfile = "3.3.0"
wasmparser = { workspace = true }
wasmprinter = { workspace = true }
wasmtime = { workspace = true, features = ['default', 'winch', 'gc', 'memory-protection-keys', 'signals-based-traps'] }
wasmtime = { workspace = true, features = ['default', 'winch', 'gc', 'memory-protection-keys'] }
wasmtime-wast = { workspace = true, features = ['component-model'] }
wasm-encoder = { workspace = true }
wasm-smith = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi-nn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ cap-std = { workspace = true }
libtest-mimic = { workspace = true }
test-programs-artifacts = { workspace = true }
wasmtime-wasi = { workspace = true, features = ["preview1"] }
wasmtime = { workspace = true, features = ["cranelift", 'signals-based-traps'] }
wasmtime = { workspace = true, features = ["cranelift"] }
tracing-subscriber = { workspace = true }

[features]
Expand Down
2 changes: 1 addition & 1 deletion crates/wasi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test-log = { workspace = true }
tracing-subscriber = { workspace = true }
test-programs-artifacts = { workspace = true }
tempfile = { workspace = true }
wasmtime = { workspace = true, features = ['cranelift', 'incremental-cache', 'signals-based-traps'] }
wasmtime = { workspace = true, features = ['cranelift', 'incremental-cache'] }

[target.'cfg(unix)'.dependencies]
rustix = { workspace = true, features = ["event", "fs", "net"] }
Expand Down
35 changes: 14 additions & 21 deletions crates/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ default = [
'component-model',
'threads',
'std',
'signals-based-traps',
]

# An on-by-default feature enabling runtime compilation of WebAssembly modules
Expand Down Expand Up @@ -193,7 +192,6 @@ async = [
pooling-allocator = [
"runtime",
"std", # not ported to no_std yet
"signals-based-traps", # pooling allocation always uses mmap at this time
]

# Enables support for all architectures in Cranelift, allowing
Expand Down Expand Up @@ -250,6 +248,7 @@ runtime = [
"dep:rustix",
"rustix/mm",
"pulley-interpreter?/interp",
"dep:wasmtime-jit-icache-coherence",
]

# Enable support for garbage collection-related things.
Expand All @@ -273,7 +272,6 @@ gc = [
"wasmtime-environ/gc",
"wasmtime-cranelift?/gc",
"wasmtime-winch?/gc",
"signals-based-traps", # not ported to non-mmap schemes yet
]

# Enable the deferred reference counting garbage collector.
Expand All @@ -297,7 +295,6 @@ threads = [
"wasmtime-cranelift?/threads",
"wasmtime-winch?/threads",
"std",
"signals-based-traps",
]

# Controls whether backtraces will attempt to parse DWARF information in
Expand All @@ -319,13 +316,6 @@ std = [
'wasmtime-fiber?/std',
'pulley-interpreter?/std',
'wasmtime-math/std',
# technically this isn't necessary but once you have the standard library you
# probably want things to go fast in which case you've probably got signal
# handlers and such so implicitly enable this. This also helps reduce the
# verbosity of others depending on `wasmtime` with `default-features = false`
# where frequently `std` is enabled and this feature will typically want to be
# enabled by default as well.
'signals-based-traps',
]

# Enables support for the `Store::call_hook` API which enables injecting custom
Expand Down Expand Up @@ -357,14 +347,17 @@ reexport-wasmparser = []
# provides a human-readable text format for component values.
wave = ["dep:wasm-wave"]

# Gates compile-time support for host signals-based-traps.
# For platforms that Wasmtime does not have support for Wasmtime will disable
# the use of virtual memory by default, for example allocating linear memories
# with `malloc` instead. This feature can be used, for these platforms, to
# instead use a C API defined in `wasmtime-platform.h` instead.
#
# Traps based on signals, such as SIGSEGV, are useful for accelerating
# WebAssembly by removing explicit checks and letting the hardware deliver
# signals instead. This feature is enabled by default and gates a number
# of implementations within Wasmtime that may rely on virtual memory, for
# example. Embedded systems or smaller systems may wish to disable this feature
# to reduce the runtime requirements of Wasmtime.
signals-based-traps = [
"dep:wasmtime-jit-icache-coherence",
]
# For some more information see
# https://docs.wasmtime.dev/stability-platform-support.html#support-for-no_std
#
# This feature is not necessary for supported platforms like Unix and Windows as
# virtual memory is always enabled there.
custom-virtual-memory = []

# Same as `custom-virtual-memory` above, but for custom signal-handling APIs.
custom-native-signals = []
49 changes: 36 additions & 13 deletions crates/wasmtime/build.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,52 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");

// NB: duplicating a workaround in the wasmtime-fiber build script.
println!("cargo:rustc-check-cfg=cfg(asan)");
if cfg_is("sanitize", "address") {
println!("cargo:rustc-cfg=asan");
}

let unix = cfg("unix");
let windows = cfg("windows");
let miri = cfg("miri");
let supported_platform = unix || windows;

let has_native_signals =
!miri && (supported_platform || cfg!(feature = "custom-native-signals"));
let has_virtual_memory = supported_platform || cfg!(feature = "custom-virtual-memory");

println!("cargo:rustc-check-cfg=cfg(has_native_signals, has_virtual_memory)");
if has_native_signals {
println!("cargo:rustc-cfg=has_native_signals");
}
if has_virtual_memory {
println!("cargo:rustc-cfg=has_virtual_memory");
}

#[cfg(feature = "runtime")]
build_c_helpers();
}

fn cfg(key: &str) -> bool {
std::env::var(&format!("CARGO_CFG_{}", key.to_uppercase())).is_ok()
}

fn cfg_is(key: &str, val: &str) -> bool {
std::env::var(&format!("CARGO_CFG_{}", key.to_uppercase()))
.ok()
.as_deref()
== Some(val)
}

#[cfg(feature = "runtime")]
fn build_c_helpers() {
use wasmtime_versioned_export_macros::versioned_suffix;

// NB: duplicating a workaround in the wasmtime-fiber build script.
println!("cargo:rustc-check-cfg=cfg(asan)");
match std::env::var("CARGO_CFG_SANITIZE") {
Ok(s) if s == "address" => {
println!("cargo:rustc-cfg=asan");
}
_ => {}
}

// If this platform is neither unix nor windows then there's no default need
// for a C helper library since `helpers.c` is tailored for just these
// platforms currently.
if std::env::var("CARGO_CFG_UNIX").is_err() && std::env::var("CARGO_CFG_WINDOWS").is_err() {
if !cfg("unix") && !cfg("windows") {
return;
}

Expand All @@ -38,9 +63,7 @@ fn build_c_helpers() {

// On MinGW targets work around a bug in the MinGW compiler described at
// https://github.com/bytecodealliance/wasmtime/pull/9688#issuecomment-2573367719
if std::env::var("CARGO_CFG_WINDOWS").is_ok()
&& std::env::var("CARGO_CFG_TARGET_ENV").ok().as_deref() == Some("gnu")
{
if cfg("windows") && cfg_is("target_env", "gnu") {
build.define("__USE_MINGW_SETJMP_NON_SEH", None);
}

Expand Down
32 changes: 17 additions & 15 deletions crates/wasmtime/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1554,7 +1554,6 @@ impl Config {
///
/// For 32-bit platforms this value defaults to 10MiB. This means that
/// bounds checks will be required on 32-bit platforms.
#[cfg(feature = "signals-based-traps")]
pub fn memory_reservation(&mut self, bytes: u64) -> &mut Self {
self.tunables.memory_reservation = Some(bytes);
self
Expand Down Expand Up @@ -1590,7 +1589,6 @@ impl Config {
/// the memory configuration works at runtime.
///
/// The default value for this option is `true`.
#[cfg(feature = "signals-based-traps")]
pub fn memory_may_move(&mut self, enable: bool) -> &mut Self {
self.tunables.memory_may_move = Some(enable);
self
Expand Down Expand Up @@ -1639,7 +1637,6 @@ impl Config {
/// allows eliminating almost all bounds checks on loads/stores with an
/// immediate offset of less than 32MiB. On 32-bit platforms this defaults
/// to 64KiB.
#[cfg(feature = "signals-based-traps")]
pub fn memory_guard_size(&mut self, bytes: u64) -> &mut Self {
self.tunables.memory_guard_size = Some(bytes);
self
Expand Down Expand Up @@ -1729,7 +1726,6 @@ impl Config {
/// ## Default
///
/// This value defaults to `true`.
#[cfg(feature = "signals-based-traps")]
pub fn guard_before_linear_memory(&mut self, enable: bool) -> &mut Self {
self.tunables.guard_before_linear_memory = Some(enable);
self
Expand Down Expand Up @@ -1845,7 +1841,6 @@ impl Config {
/// [`Module::deserialize_file`]: crate::Module::deserialize_file
/// [`Module`]: crate::Module
/// [IPI]: https://en.wikipedia.org/wiki/Inter-processor_interrupt
#[cfg(feature = "signals-based-traps")]
pub fn memory_init_cow(&mut self, enable: bool) -> &mut Self {
self.tunables.memory_init_cow = Some(enable);
self
Expand Down Expand Up @@ -2113,13 +2108,19 @@ impl Config {

let mut tunables = Tunables::default_for_target(&self.compiler_target())?;

// When signals-based traps are disabled use slightly different defaults
// If this platform doesn't have native signals then change some
// defaults to account for that. Note that VM guards are turned off here
// because that's primarily a feature of eliding bounds-checks.
if !cfg!(has_native_signals) {
tunables.signals_based_traps = cfg!(has_native_signals);
tunables.memory_guard_size = 0;
}

// When virtual memory is not available use slightly different defaults
// for tunables to be more amenable to `MallocMemory`. Note that these
// can still be overridden by config options.
if !cfg!(feature = "signals-based-traps") {
tunables.signals_based_traps = false;
if !cfg!(has_virtual_memory) {
tunables.memory_reservation = 0;
tunables.memory_guard_size = 0;
tunables.memory_reservation_for_growth = 1 << 20; // 1MB
tunables.memory_init_cow = false;
}
Expand Down Expand Up @@ -2148,11 +2149,13 @@ impl Config {
None
};

// These `Config` accessors are disabled at compile time so double-check
// the defaults here.
if !cfg!(feature = "signals-based-traps") {
assert!(!tunables.signals_based_traps);
assert!(!tunables.memory_init_cow);
// Double-check that this configuration isn't requesting capabilities
// that this build of Wasmtime doesn't support.
if !cfg!(has_native_signals) && tunables.signals_based_traps {
bail!("signals-based-traps disabled at compile time -- cannot be enabled");
}
if !cfg!(has_virtual_memory) && tunables.memory_init_cow {
bail!("virtual memory disabled at compile time -- cannot enable CoW");
}

Ok((tunables, features))
Expand Down Expand Up @@ -2500,7 +2503,6 @@ impl Config {
/// are enabled by default.
///
/// **Note** Disabling this option is not compatible with the Winch compiler.
#[cfg(feature = "signals-based-traps")]
pub fn signals_based_traps(&mut self, enable: bool) -> &mut Self {
self.tunables.signals_based_traps = Some(enable);
self
Expand Down
Loading

0 comments on commit 033488a

Please sign in to comment.