diff --git a/Cargo.toml b/Cargo.toml index c9caf5d3754..8cff8153616 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -177,6 +177,11 @@ name = "engine-headless" path = "examples/engine_headless.rs" required-features = ["cranelift"] +[[example]] +name = "engine-headless-ios" +path = "examples/engine_headless_ios.rs" +required-features = ["cranelift"] + [[example]] name = "cross-compilation" path = "examples/engine_cross_compilation.rs" diff --git a/Makefile b/Makefile index f0d1a1fb813..1872aa5e7b0 100644 --- a/Makefile +++ b/Makefile @@ -483,6 +483,10 @@ build-capi-headless-all: capi-setup RUSTFLAGS="${RUSTFLAGS}" cargo build --manifest-path lib/c-api/Cargo.toml --release \ --no-default-features --features universal,dylib,staticlib,wasi +build-capi-headless-ios: capi-setup + RUSTFLAGS="${RUSTFLAGS}" cargo lipo --manifest-path lib/c-api/Cargo.toml --release \ + --no-default-features --features dylib,wasi + ##### # # Testing. diff --git a/examples/engine_headless_ios.rs b/examples/engine_headless_ios.rs new file mode 100644 index 00000000000..1877d6d76b7 --- /dev/null +++ b/examples/engine_headless_ios.rs @@ -0,0 +1,155 @@ +//! Defining an engine in Wasmer is one of the fundamental steps. +//! +//! This example illustrates a neat feature of engines: their ability +//! to run in a headless mode. At the time of writing, all engines +//! have a headless mode, but it's not a requirement of the `Engine` +//! trait (defined in the `wasmer_engine` crate). +//! +//! What problem does it solve, and what does it mean? +//! +//! Once a Wasm module is compiled into executable code and stored +//! somewhere (e.g. in memory with the Universal engine, or in a +//! shared object file with the Dylib engine), the module can be +//! instantiated and executed. But imagine for a second the following +//! scenario: +//! +//! * Modules are compiled ahead of time, to be instantiated later +//! on. +//! * Modules are cross-compiled on a machine ahead of time +//! to be run on another machine later one. +//! +//! In both scenarios, the environment where the compiled Wasm module +//! will be executed can be very constrained. For such particular +//! contexts, Wasmer can be compiled _without_ the compilers, so that +//! the `wasmer` binary is as small as possible. Indeed, there is no +//! need for a compiler since the Wasm module is already compiled. All +//! we need is an engine that _only_ drives the instantiation and +//! execution of the Wasm module. +//! +//! And that, that's a headless engine. +//! +//! To achieve such a scenario, a Wasm module must be compiled, then +//! serialized —for example into a file—, then later, potentially on +//! another machine, deserialized. The next steps are classical: The +//! Wasm module is instantiated and executed. +//! +//! This example uses a `compiler` because it illustrates the entire +//! workflow, but keep in mind the compiler isn't required after the +//! compilation step. +//! +//! You can run the example directly by executing in Wasmer root: +//! +//! ```shell +//! cargo run --example engine-headless --release --features "cranelift" +//! ``` +//! +//! Ready? + +use std::fs::File; +use std::path::Path; +use std::str::FromStr; +use tempfile::NamedTempFile; +use wasmer::imports; +use wasmer::wat2wasm; +use wasmer::Instance; +use wasmer::Module; +use wasmer::RuntimeError; +use wasmer::Store; +use wasmer::Value; +use wasmer_compiler::{CpuFeature, Target, Triple}; +use wasmer_compiler_cranelift::Cranelift; +use wasmer_compiler_llvm::LLVM; +use wasmer_engine_dylib::Dylib; + +fn main() -> Result<(), Box> { + // First step, let's compile the Wasm module and serialize it. + // Note: we need a compiler here. + let serialized_module_file = { + // Let's declare the Wasm module with the text representation. + let wasm_bytes = wat2wasm( + r#" +(module + (type $sum_t (func (param i32 i32) (result i32))) + (func $sum_f (type $sum_t) (param $x i32) (param $y i32) (result i32) + local.get $x + local.get $y + i32.add) + (export "sum" (func $sum_f))) +"# + .as_bytes(), + )?; + + let compiler_config = LLVM::default(); + let triple = Triple::from_str("aarch64-apple-ios") + .map_err(|error| RuntimeError::new(error.to_string()))?; + + // Let's define a CPU feature. + let mut cpu_feature = CpuFeature::set(); + cpu_feature.insert(CpuFeature::from_str("sse2")?); + + // Let's build the target. + let target = Target::new(triple, cpu_feature); + println!("Chosen target: {:?}", target); + + println!("Creating Dylib engine..."); + let engine = Dylib::new(compiler_config).target(target).engine(); + + // Create a store, that holds the engine. + let store = Store::new(&engine); + + println!("Compiling module..."); + // Let's compile the Wasm module. + let module = Module::new(&store, wasm_bytes)?; + // Here we go. Let's serialize the compiled Wasm module in a + // file. + println!("Serializing module..."); + let mut dylib_file = Path::new("./sum.dylib"); + module.serialize_to_file(dylib_file)?; + dylib_file + }; + + // Second step, deserialize the compiled Wasm module, and execute + // it, for example with Wasmer without a compiler. + { + println!("Creating headless Dylib engine..."); + // We create a headless Dylib engine. + let engine = Dylib::headless().engine(); + let store = Store::new(&engine); + + println!("Deserializing module..."); + // Here we go. + // + // Deserialize the compiled Wasm module. This code is unsafe + // because Wasmer can't assert the bytes are valid (see the + // `wasmer::Module::deserialize`'s documentation to learn + // more). + let module = unsafe { Module::deserialize_from_file(&store, dylib_file) }?; + println!("Done..."); + // Congrats, the Wasm module has been deserialized! Now let's + // execute it for the sake of having a complete example. + + // Create an import object. Since our Wasm module didn't declare + // any imports, it's an empty object. + let import_object = imports! {}; + + // println!("Instantiating module..."); + // Let's instantiate the Wasm module. + // let instance = Instance::new(&module, &import_object)?; + + // println!("Calling `sum` function..."); + // The Wasm module exports a function called `sum`. + // let sum = instance.exports.get_function("sum")?; + // let results = sum.call(&[Value::I32(1), Value::I32(2)])?; + + // println!("Results: {:?}", results); + // assert_eq!(results.to_vec(), vec![Value::I32(3)]); + } + + Ok(()) +} + +#[test] +#[cfg(not(any(windows, target_arch = "aarch64", target_env = "musl")))] +fn test_engine_headless_ios() -> Result<(), Box> { + main() +} diff --git a/lib/c-api/build.rs b/lib/c-api/build.rs index ce423ac12e1..1af83f9ff71 100644 --- a/lib/c-api/build.rs +++ b/lib/c-api/build.rs @@ -255,7 +255,7 @@ fn build_inline_c_env_vars() { shared_object_dir = shared_object_dir, lib = if cfg!(target_os = "windows") { "wasmer.dll".to_string() - } else if cfg!(target_os = "macos") { + } else if cfg!(target_vendor = "apple") { "libwasmer.dylib".to_string() } else { let path = format!( diff --git a/lib/c-api/src/wasm_c_api/engine.rs b/lib/c-api/src/wasm_c_api/engine.rs index 058697393b0..0cfee66c441 100644 --- a/lib/c-api/src/wasm_c_api/engine.rs +++ b/lib/c-api/src/wasm_c_api/engine.rs @@ -23,7 +23,6 @@ use wasmer_engine_universal::Universal; /// /// This is a Wasmer-specific type with Wasmer-specific functions for /// manipulating it. -#[cfg(feature = "compiler")] #[derive(Debug, Copy, Clone)] #[repr(C)] pub enum wasmer_compiler_t { diff --git a/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs b/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs index 377fa332f9a..3349984a361 100644 --- a/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs +++ b/lib/c-api/src/wasm_c_api/unstable/parser/operator.rs @@ -1,4 +1,4 @@ -use wasmer_api::wasmparser::Operator; +use wasmparser::Operator; #[repr(C)] #[allow(non_camel_case_types)] diff --git a/lib/compiler-llvm/src/translator/stackmap.rs b/lib/compiler-llvm/src/translator/stackmap.rs index d23ebd1a57e..cb6abc48fb5 100644 --- a/lib/compiler-llvm/src/translator/stackmap.rs +++ b/lib/compiler-llvm/src/translator/stackmap.rs @@ -54,7 +54,7 @@ pub enum StackmapEntryKind { impl StackmapEntry { #[cfg(all( - any(target_os = "freebsd", target_os = "linux", target_os = "macos"), + any(target_os = "freebsd", target_os = "linux", target_vendor = "apple"), target_arch = "x86_64" ))] pub fn populate_msm( diff --git a/lib/emscripten/src/syscalls/unix.rs b/lib/emscripten/src/syscalls/unix.rs index ee4874acf65..265aabf28a3 100644 --- a/lib/emscripten/src/syscalls/unix.rs +++ b/lib/emscripten/src/syscalls/unix.rs @@ -1,5 +1,5 @@ use crate::{ptr::WasmPtr, varargs::VarArgs, LibcDirWrapper}; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::size_t; /// NOTE: TODO: These syscalls only support wasm_32 for now because they assume offsets are u32 /// Syscall list: https://www.cs.utexas.edu/~bismith/test/syscalls/syscalls32.html @@ -121,7 +121,7 @@ use std::io::Error; use std::mem; // Linking to functions that are not provided by rust libc -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] #[link(name = "c")] extern "C" { pub fn wait4(pid: pid_t, status: *mut c_int, options: c_int, rusage: *mut rusage) -> pid_t; @@ -140,19 +140,19 @@ extern "C" { pub fn lstat(path: *const libc::c_char, buf: *mut stat) -> c_int; } -#[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "android")))] +#[cfg(not(any(target_os = "freebsd", target_vendor = "apple", target_os = "android")))] use libc::fallocate; #[cfg(target_os = "freebsd")] use libc::madvise; -#[cfg(not(any(target_os = "freebsd", target_os = "macos")))] +#[cfg(not(any(target_os = "freebsd", target_vendor = "apple")))] use libc::{fdatasync, ftruncate64, lstat, madvise, wait4}; // Another conditional constant for name resolution: Macos et iOS use // SO_NOSIGPIPE as a setsockopt flag to disable SIGPIPE emission on socket. // Other platforms do otherwise. -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::SO_NOSIGPIPE; -#[cfg(not(target_os = "macos"))] +#[cfg(not(target_vendor = "apple"))] const SO_NOSIGPIPE: c_int = 0; /// open @@ -269,7 +269,7 @@ pub fn ___syscall194(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int debug!("emscripten::___syscall194 (ftruncate64) {}", _which); let _fd: c_int = varargs.get(ctx); let _length: i64 = varargs.get(ctx); - #[cfg(not(any(target_os = "freebsd", target_os = "macos")))] + #[cfg(not(any(target_os = "freebsd", target_vendor = "apple")))] unsafe { ftruncate64(_fd, _length) } @@ -277,7 +277,7 @@ pub fn ___syscall194(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int unsafe { ftruncate(_fd, _length) } - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which) } @@ -639,7 +639,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let mut host_address: sockaddr = sockaddr { sa_family: Default::default(), sa_data: Default::default(), - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] sa_len: Default::default(), }; let fd = unsafe { accept(socket, &mut host_address, address_len_addr) }; @@ -672,7 +672,7 @@ pub fn ___syscall102(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let mut sock_addr_host: sockaddr = sockaddr { sa_family: Default::default(), sa_data: Default::default(), - #[cfg(any(target_os = "freebsd", target_os = "macos"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple"))] sa_len: Default::default(), }; let ret = unsafe { @@ -1013,14 +1013,14 @@ pub fn ___syscall196(ctx: &EmEnv, _which: i32, mut varargs: VarArgs) -> i32 { unsafe { let mut stat: stat = std::mem::zeroed(); - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] let stat_ptr = &mut stat as *mut stat as *mut c_void; - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] let stat_ptr = &mut stat as *mut stat; - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] let ret = lstat64(real_path, stat_ptr); - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] let ret = lstat(real_path, stat_ptr); debug!("ret: {}", ret); @@ -1131,11 +1131,11 @@ pub fn ___syscall324(ctx: &EmEnv, _which: c_int, mut varargs: VarArgs) -> c_int let _mode: c_int = varargs.get(ctx); let _offset: off_t = varargs.get(ctx); let _len: off_t = varargs.get(ctx); - #[cfg(not(any(target_os = "freebsd", target_os = "macos", target_os = "android")))] + #[cfg(not(any(target_os = "freebsd", target_vendor = "apple", target_os = "android")))] unsafe { fallocate(_fd, _mode, _offset, _len) } - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "android"))] + #[cfg(any(target_os = "freebsd", target_vendor = "apple", target_os = "android"))] { unimplemented!("emscripten::___syscall324 (fallocate) {}", _which) } diff --git a/lib/emscripten/src/time.rs b/lib/emscripten/src/time.rs index 1827b89b1ee..7bb57e68d0c 100644 --- a/lib/emscripten/src/time.rs +++ b/lib/emscripten/src/time.rs @@ -35,11 +35,11 @@ use libc::{CLOCK_MONOTONIC, CLOCK_REALTIME}; #[cfg(target_os = "freebsd")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] use libc::CLOCK_REALTIME; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] const CLOCK_MONOTONIC: clockid_t = 1; -#[cfg(target_os = "macos")] +#[cfg(target_vendor = "apple")] const CLOCK_MONOTONIC_COARSE: clockid_t = 6; // some assumptions about the constants when targeting windows diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index e60704d02ed..301568e4b2e 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -858,9 +858,9 @@ impl LibCall { Self::RaiseTrap => "wasmer_vm_raise_trap", // We have to do this because macOS requires a leading `_` and it's not // a normal function, it's a static variable, so we have to do it manually. - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] Self::Probestack => "_wasmer_vm_probestack", - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] Self::Probestack => "wasmer_vm_probestack", } } diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index aff2b443a23..4a4c7a0f1f3 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -82,12 +82,12 @@ cfg_if::cfg_if! { // On ARM, handle Unaligned Accesses. // On Darwin, guard page accesses are raised as SIGBUS. - if cfg!(target_arch = "arm") || cfg!(target_os = "macos") { + if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") { register(&mut PREV_SIGBUS, libc::SIGBUS); } } - #[cfg(target_os = "macos")] + #[cfg(target_vendor = "apple")] unsafe fn thread_stack() -> (usize, usize) { let this_thread = libc::pthread_self(); let stackaddr = libc::pthread_get_stackaddr_np(this_thread); @@ -95,7 +95,7 @@ cfg_if::cfg_if! { (stackaddr as usize - stacksize, stacksize) } - #[cfg(not(target_os = "macos"))] + #[cfg(not(target_vendor = "apple"))] unsafe fn thread_stack() -> (usize, usize) { let this_thread = libc::pthread_self(); let mut thread_attrs: libc::pthread_attr_t = mem::zeroed(); @@ -219,10 +219,10 @@ cfg_if::cfg_if! { } else if #[cfg(all(target_os = "android", target_arch = "aarch64"))] { let cx = &*(cx as *const libc::ucontext_t); cx.uc_mcontext.pc as *const u8 - } else if #[cfg(all(target_os = "macos", target_arch = "x86_64"))] { + } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { let cx = &*(cx as *const libc::ucontext_t); (*cx.uc_mcontext).__ss.__rip as *const u8 - } else if #[cfg(all(target_os = "macos", target_arch = "aarch64"))] { + } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { use std::mem; // TODO: This should be integrated into rust/libc // Related issue: https://github.com/rust-lang/libc/issues/1977 diff --git a/lib/wasi/src/syscalls/mod.rs b/lib/wasi/src/syscalls/mod.rs index e172ca5782e..921ef315d06 100644 --- a/lib/wasi/src/syscalls/mod.rs +++ b/lib/wasi/src/syscalls/mod.rs @@ -8,7 +8,7 @@ pub mod types { target_os = "freebsd", target_os = "linux", target_os = "android", - target_os = "macos" + target_vendor = "apple" ))] pub mod unix; #[cfg(any(target_os = "windows"))] @@ -36,7 +36,7 @@ use wasmer::{Memory, RuntimeError, Value, WasmCell}; target_os = "freebsd", target_os = "linux", target_os = "android", - target_os = "macos" + target_vendor = "apple" ))] pub use unix::*;