Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS Support - via .dylib & Headless Runtime #2516

Closed
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
39e014a
Add iOS support for Headless Wasmer
NathHorrigan Aug 11, 2021
ab3b0b5
Revert `engine_headless.rs`
NathHorrigan Aug 11, 2021
3c8d8bd
Add dedicated iOS example file
NathHorrigan Aug 11, 2021
35fd710
Remove cargo changes
NathHorrigan Aug 11, 2021
9c31c68
Add custom linker flags for iOS
NathHorrigan Aug 12, 2021
e91629a
Rename dylib identifier when writing to file
NathHorrigan Aug 12, 2021
ba9effb
Add @executable_path to linked identifer
NathHorrigan Aug 12, 2021
142b281
Alter dylib rename command
NathHorrigan Aug 12, 2021
9473f54
Clean up ios example
NathHorrigan Aug 12, 2021
f144f62
Switch from macos to apple target_vendor for is_deserializable
NathHorrigan Aug 12, 2021
a208185
Add compiler flags around wasmer_compiler_t bug
NathHorrigan Aug 12, 2021
8a41c0a
Rename iOS example file
NathHorrigan Aug 12, 2021
521087f
Switch back to using wasmer_api::wasmerparser dependency.
NathHorrigan Aug 12, 2021
c6f5c64
Change `fuse_linker` config
NathHorrigan Aug 12, 2021
bc1bd62
Add description for dylib identifier renaming
NathHorrigan Aug 12, 2021
e2e322d
Add description to platform_ios_headless.rs
NathHorrigan Aug 12, 2021
a36259b
Only run target ios test on macos
NathHorrigan Aug 12, 2021
a0e77fb
Add iOS test
NathHorrigan Aug 17, 2021
a8dfe55
Flatten imports
NathHorrigan Aug 17, 2021
18d5216
Fix extension unwrapping bug
NathHorrigan Aug 17, 2021
6653c32
Improved headless support on c-api
syrusakbary Aug 17, 2021
939624a
Fixed iOS target in example
syrusakbary Aug 17, 2021
58d5296
Add target detection to. xcode
NathHorrigan Aug 18, 2021
63d011a
Update main.yaml
NathHorrigan Aug 18, 2021
057d9f7
Remove commented out code
NathHorrigan Aug 18, 2021
ac74ac6
Fix wrong command in make file
NathHorrigan Aug 18, 2021
50b3420
Remove unnessacery imports in testing header
NathHorrigan Aug 18, 2021
be56584
M1 Fixes
NathHorrigan Aug 18, 2021
a3bbedb
Updated version
syrusakbary Aug 18, 2021
08bb286
Fix makefile again…
NathHorrigan Aug 18, 2021
50a8db8
Disable BitCode
NathHorrigan Aug 18, 2021
e3194dd
Exlude arm64 for sim
NathHorrigan Aug 18, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved
RUSTFLAGS="${RUSTFLAGS}" cargo lipo --manifest-path lib/c-api/Cargo.toml --release \
--no-default-features --features dylib,wasi

#####
#
# Testing.
Expand Down
155 changes: 155 additions & 0 deletions examples/engine_headless_ios.rs
Original file line number Diff line number Diff line change
@@ -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?
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved

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<dyn std::error::Error>> {
// First step, let's compile the Wasm module and serialize it.
// Note: we need a compiler here.
let mut dylib_file = Path::new("./sum.dylib");
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()))?;
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved

// Let's define a CPU feature.
let mut cpu_feature = CpuFeature::set();
cpu_feature.insert(CpuFeature::from_str("sse2")?);
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved

// 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)?;

println!("Serializing module...");
// Here we go. Let's serialize the compiled Wasm module in a
// file.
module.serialize_to_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")))]
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved
fn test_engine_headless() -> Result<(), Box<dyn std::error::Error>> {
main()
}
1 change: 1 addition & 0 deletions lib/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ thiserror = "1.0"
more-asserts = "0.2"
# - Optional shared dependencies.
wat = { version = "1.0", optional = true }
wasmparser = { version = "0.78", default-features = false, optional = true }
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved

# Dependencies and Development Dependencies for `sys`.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand Down
2 changes: 2 additions & 0 deletions lib/c-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ crate-type = ["cdylib", "rlib", "staticlib"]
# We rename `wasmer` to `wasmer-api` to avoid the conflict with this
# library name (see `[lib]`).
wasmer-api = { version = "2.0.0", path = "../api", default-features = false, package = "wasmer" }
wasmparser = { version = "0.78", default-features = false, optional = false }

NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved
wasmer-compiler-cranelift = { version = "2.0.0", path = "../compiler-cranelift", optional = true }
wasmer-compiler-singlepass = { version = "2.0.0", path = "../compiler-singlepass", optional = true }
wasmer-compiler-llvm = { version = "2.0.0", path = "../compiler-llvm", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion lib/c-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!(
Expand Down
1 change: 0 additions & 1 deletion lib/c-api/src/wasm_c_api/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undelete this :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing so causes this:

error[E0432]: unresolved import `super::super::engine::wasmer_compiler_t`
 --> lib/c-api/src/wasm_c_api/unstable/engine.rs:4:43
  |
4 | use super::super::engine::{wasm_config_t, wasmer_compiler_t, wasmer_engine_t};
  |                                           ^^^^^^^^^^^^^^^^^ no `wasmer_compiler_t` in `wasm_c_api::engine`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is used by this method:

pub extern "C" fn wasmer_is_compiler_available(compiler: wasmer_compiler_t) -> bool {
    match compiler {
        wasmer_compiler_t::CRANELIFT if cfg!(feature = "cranelift") => true,
        wasmer_compiler_t::LLVM if cfg!(feature = "llvm") => true,
        wasmer_compiler_t::SINGLEPASS if cfg!(feature = "singlepass") => true,
        _ => false,
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, keep #[cfg(feature = "compiler")] here. But add:

use super::super::engine:{wasm_config_t, wasmer_engine_t};
#[cfg(feature = "compiler")]
use super::super::engine::wasmer_compiler_t;

in lib/c-api/src/wasm_c_api/unstable/engine.rs. It's a bug here.

Thank you very much!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added those compiler conditions, I had to add some others to fix more build errors though?

#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub enum wasmer_compiler_t {
Expand Down
2 changes: 1 addition & 1 deletion lib/c-api/src/wasm_c_api/unstable/parser/operator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use wasmer_api::wasmparser::Operator;
use wasmparser::Operator;
NathHorrigan marked this conversation as resolved.
Show resolved Hide resolved

#[repr(C)]
#[allow(non_camel_case_types)]
Expand Down
2 changes: 1 addition & 1 deletion lib/compiler-llvm/src/translator/stackmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
32 changes: 16 additions & 16 deletions lib/emscripten/src/syscalls/unix.rs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -269,15 +269,15 @@ 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)
}
#[cfg(target_os = "freebsd")]
unsafe {
ftruncate(_fd, _length)
}
#[cfg(target_os = "macos")]
#[cfg(target_vendor = "apple")]
unimplemented!("emscripten::___syscall194 (ftruncate64) {}", _which)
}

Expand Down Expand Up @@ -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) };
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 3 additions & 3 deletions lib/emscripten/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions lib/vm/src/libcalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}
}
Expand Down
10 changes: 5 additions & 5 deletions lib/vm/src/trap/traphandlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ 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);
let stacksize = libc::pthread_get_stacksize_np(this_thread);
(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();
Expand Down Expand Up @@ -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
Expand Down
Loading