diff --git a/CHANGELOG.md b/CHANGELOG.md index dfedc2635df..8ab0bb7fada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## **[Unreleased]** +- [#1062](https://github.com/wasmerio/wasmer/pull/1062) Expose some opt-in Emscripten functions to the C API +- [#1032](https://github.com/wasmerio/wasmer/pull/1032) Change the signature of the Emscripten `abort` function to work with Emscripten 1.38.30 - [#1060](https://github.com/wasmerio/wasmer/pull/1060) Test the capi with all the backends - [#1069](https://github.com/wasmerio/wasmer/pull/1069) Add function `get_memory_and_data` to `Ctx` to help prevent undefined behavior and mutable aliasing. It allows accessing memory while borrowing data mutably for the `Ctx` lifetime. This new function is now being used in `wasmer-wasi`. - [#1058](https://github.com/wasmerio/wasmer/pull/1058) Fix minor panic issue when `wasmer::compile_with` called with llvm backend. diff --git a/Cargo.lock b/Cargo.lock index adf3da3acd5..eedc2310d7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1484,6 +1484,7 @@ version = "0.11.0" dependencies = [ "cbindgen 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmer-emscripten 0.11.0", "wasmer-runtime 0.11.0", "wasmer-runtime-core 0.11.0", "wasmer-wasi 0.11.0", diff --git a/Makefile b/Makefile index d0dc4ec1bb7..4b754c724be 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,10 @@ capi-llvm: cargo build --manifest-path lib/runtime-c-api/Cargo.toml --release \ --no-default-features --features llvm-backend,wasi +capi-emscripten: + cargo build --manifest-path lib/runtime-c-api/Cargo.toml --release \ + --no-default-features --features singlepass-backend,emscripten + # We use cranelift as the default backend for the capi for now capi: capi-cranelift @@ -129,7 +133,11 @@ test-capi-llvm: capi-llvm cargo test --manifest-path lib/runtime-c-api/Cargo.toml --release \ --no-default-features --features llvm-backend,wasi -test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm +test-capi-emscripten: capi-emscripten + cargo test --manifest-path lib/runtime-c-api/Cargo.toml --release \ + --no-default-features --features singlepass-backend,emscripten + +test-capi: test-capi-singlepass test-capi-cranelift test-capi-llvm test-capi-emscripten capi-test: test-capi diff --git a/lib/emscripten/src/lib.rs b/lib/emscripten/src/lib.rs index a421a0f90cf..289835ff955 100644 --- a/lib/emscripten/src/lib.rs +++ b/lib/emscripten/src/lib.rs @@ -325,6 +325,61 @@ impl<'a> EmscriptenData<'a> { } } +/// Call the global constructors for C++ and set up the emscripten environment. +/// +/// Note that this function does not completely set up Emscripten to be called. +/// before calling this function, please initialize `Ctx::data` with a pointer +/// to [`EmscriptenData`]. +pub fn set_up_emscripten(instance: &mut Instance) -> CallResult<()> { + // ATINIT + // (used by C++) + if let Ok(_func) = instance.dyn_func("globalCtors") { + instance.call("globalCtors", &[])?; + } + + if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { + instance.call("___emscripten_environ_constructor", &[])?; + } + Ok(()) +} + +/// Call the main function in emscripten, assumes that the emscripten state is +/// set up. +/// +/// If you don't want to set it up yourself, consider using [`run_emscripten_instance`]. +pub fn emscripten_call_main(instance: &mut Instance, path: &str, args: &[&str]) -> CallResult<()> { + let (func_name, main_func) = match instance.dyn_func("_main") { + Ok(func) => Ok(("_main", func)), + Err(_e) => match instance.dyn_func("main") { + Ok(func) => Ok(("main", func)), + Err(e) => Err(e), + }, + }?; + let num_params = main_func.signature().params().len(); + let _result = match num_params { + 2 => { + let mut new_args = vec![path]; + new_args.extend(args); + let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); + instance.call( + func_name, + &[Value::I32(argc as i32), Value::I32(argv as i32)], + )?; + } + 0 => { + instance.call(func_name, &[])?; + } + _ => { + return Err(CallError::Resolve(ResolveError::ExportWrongType { + name: "main".to_string(), + })) + } + }; + + Ok(()) +} + +/// Top level function to execute emscripten pub fn run_emscripten_instance( _module: &Module, instance: &mut Instance, @@ -338,15 +393,7 @@ pub fn run_emscripten_instance( let data_ptr = &mut data as *mut _ as *mut c_void; instance.context_mut().data = data_ptr; - // ATINIT - // (used by C++) - if let Ok(_func) = instance.dyn_func("globalCtors") { - instance.call("globalCtors", &[])?; - } - - if let Ok(_func) = instance.dyn_func("___emscripten_environ_constructor") { - instance.call("___emscripten_environ_constructor", &[])?; - } + set_up_emscripten(instance)?; // println!("running emscripten instance"); @@ -356,33 +403,7 @@ pub fn run_emscripten_instance( //let (argc, argv) = store_module_arguments(instance.context_mut(), args); instance.call(&ep, &[Value::I32(arg as i32)])?; } else { - let (func_name, main_func) = match instance.dyn_func("_main") { - Ok(func) => Ok(("_main", func)), - Err(_e) => match instance.dyn_func("main") { - Ok(func) => Ok(("main", func)), - Err(e) => Err(e), - }, - }?; - let num_params = main_func.signature().params().len(); - let _result = match num_params { - 2 => { - let mut new_args = vec![path]; - new_args.extend(args); - let (argc, argv) = store_module_arguments(instance.context_mut(), new_args); - instance.call( - func_name, - &[Value::I32(argc as i32), Value::I32(argv as i32)], - )?; - } - 0 => { - instance.call(func_name, &[])?; - } - _ => { - return Err(CallError::Resolve(ResolveError::ExportWrongType { - name: "main".to_string(), - })) - } - }; + emscripten_call_main(instance, path, &args)?; } // TODO atexit for emscripten diff --git a/lib/runtime-c-api/Cargo.toml b/lib/runtime-c-api/Cargo.toml index 39046d3d187..7e07ca36578 100644 --- a/lib/runtime-c-api/Cargo.toml +++ b/lib/runtime-c-api/Cargo.toml @@ -32,6 +32,11 @@ path = "../wasi" version = "0.11.0" optional = true +[dependencies.wasmer-emscripten] +path = "../emscripten" +version = "0.11.0" +optional = true + [features] default = ["cranelift-backend", "wasi"] debug = ["wasmer-runtime/debug"] @@ -39,6 +44,7 @@ singlepass-backend = ["wasmer-runtime/singlepass", "wasmer-runtime/default-backe cranelift-backend = ["wasmer-runtime/cranelift", "wasmer-runtime/default-backend-cranelift"] llvm-backend = ["wasmer-runtime/llvm", "wasmer-runtime/default-backend-llvm"] wasi = ["wasmer-wasi"] +emscripten = ["wasmer-emscripten"] [build-dependencies] cbindgen = "0.9" diff --git a/lib/runtime-c-api/build.rs b/lib/runtime-c-api/build.rs index 5897001b043..b20a9acd7ec 100644 --- a/lib/runtime-c-api/build.rs +++ b/lib/runtime-c-api/build.rs @@ -12,7 +12,7 @@ fn main() { let mut out_wasmer_header_file = PathBuf::from(&out_dir); out_wasmer_header_file.push("wasmer"); - const WASMER_PRE_HEADER: &str = r#" + let mut pre_header = r#" #if !defined(WASMER_H_MACROS) #define WASMER_H_MACROS @@ -28,17 +28,27 @@ fn main() { #endif #endif -#endif // WASMER_H_MACROS -"#; +"# + .to_string(); + + #[cfg(feature = "emscripten")] + { + pre_header += "#define WASMER_EMSCRIPTEN_ENABLED\n"; + } + + // close pre header + pre_header += "#endif // WASMER_H_MACROS\n"; + // Generate the C bindings in the `OUT_DIR`. out_wasmer_header_file.set_extension("h"); Builder::new() .with_crate(crate_dir.clone()) .with_language(Language::C) .with_include_guard("WASMER_H") - .with_header(WASMER_PRE_HEADER) + .with_header(&pre_header) .with_define("target_family", "windows", "_WIN32") .with_define("target_arch", "x86_64", "ARCH_X86_64") + .with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED") .generate() .expect("Unable to generate C bindings") .write_to_file(out_wasmer_header_file.as_path()); @@ -49,9 +59,10 @@ fn main() { .with_crate(crate_dir) .with_language(Language::Cxx) .with_include_guard("WASMER_H") - .with_header(WASMER_PRE_HEADER) + .with_header(&pre_header) .with_define("target_family", "windows", "_WIN32") .with_define("target_arch", "x86_64", "ARCH_X86_64") + .with_define("feature", "emscripten", "WASMER_EMSCRIPTEN_ENABLED") .generate() .expect("Unable to generate C++ bindings") .write_to_file(out_wasmer_header_file.as_path()); diff --git a/lib/runtime-c-api/src/import/emscripten.rs b/lib/runtime-c-api/src/import/emscripten.rs new file mode 100644 index 00000000000..83777a762a5 --- /dev/null +++ b/lib/runtime-c-api/src/import/emscripten.rs @@ -0,0 +1,145 @@ +//! Functions and types for dealing with Emscripten imports + +use super::*; +use crate::{get_slice_checked, instance::wasmer_instance_t, module::wasmer_module_t}; + +use std::ptr; +use wasmer_emscripten::{EmscriptenData, EmscriptenGlobals}; +use wasmer_runtime::{Instance, Module}; + +/// Type used to construct an import_object_t with Emscripten imports. +#[repr(C)] +pub struct wasmer_emscripten_globals_t; + +/// Create a `wasmer_emscripten_globals_t` from a Wasm module. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_get_globals( + module: *const wasmer_module_t, +) -> *mut wasmer_emscripten_globals_t { + if module.is_null() { + return ptr::null_mut(); + } + let module = &*(module as *const Module); + match EmscriptenGlobals::new(module) { + Ok(globals) => Box::into_raw(Box::new(globals)) as *mut wasmer_emscripten_globals_t, + Err(msg) => { + update_last_error(CApiError { msg }); + return ptr::null_mut(); + } + } +} + +/// Destroy `wasmer_emscrpten_globals_t` created by +/// `wasmer_emscripten_get_emscripten_globals`. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_destroy_globals( + globals: *mut wasmer_emscripten_globals_t, +) { + if globals.is_null() { + return; + } + let _ = Box::from_raw(globals); +} + +/// Execute global constructors (required if the module is compiled from C++) +/// and sets up the internal environment. +/// +/// This function sets the data pointer in the same way that +/// [`wasmer_instance_context_data_set`] does. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_set_up( + instance: *mut wasmer_instance_t, + globals: *mut wasmer_emscripten_globals_t, +) -> wasmer_result_t { + if globals.is_null() || instance.is_null() { + return wasmer_result_t::WASMER_ERROR; + } + let instance = &mut *(instance as *mut Instance); + let globals = &*(globals as *mut EmscriptenGlobals); + let em_data = Box::into_raw(Box::new(EmscriptenData::new( + instance, + &globals.data, + Default::default(), + ))) as *mut c_void; + instance.context_mut().data = em_data; + + match wasmer_emscripten::set_up_emscripten(instance) { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(e) => { + update_last_error(e); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Convenience function for setting up arguments and calling the Emscripten +/// main function. +/// +/// WARNING: +/// +/// Do not call this function on untrusted code when operating without +/// additional sandboxing in place. +/// Emscripten has access to many host system calls and therefore may do very +/// bad things. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_call_main( + instance: *mut wasmer_instance_t, + args: *const wasmer_byte_array, + args_len: c_uint, +) -> wasmer_result_t { + if instance.is_null() || args.is_null() { + return wasmer_result_t::WASMER_ERROR; + } + let instance = &mut *(instance as *mut Instance); + + let arg_list = get_slice_checked(args, args_len as usize); + let arg_process_result: Result, _> = + arg_list.iter().map(|arg| arg.as_str()).collect(); + let arg_vec = match arg_process_result.as_ref() { + Ok(arg_vec) => arg_vec, + Err(err) => { + update_last_error(*err); + return wasmer_result_t::WASMER_ERROR; + } + }; + + let prog_name = if let Some(prog_name) = arg_vec.first() { + prog_name + } else { + update_last_error(CApiError { + msg: "First argument (program name) is required to execute Emscripten's main function" + .to_string(), + }); + return wasmer_result_t::WASMER_ERROR; + }; + + match wasmer_emscripten::emscripten_call_main(instance, prog_name, &arg_vec[1..]) { + Ok(_) => wasmer_result_t::WASMER_OK, + Err(e) => { + update_last_error(e); + wasmer_result_t::WASMER_ERROR + } + } +} + +/// Create a `wasmer_import_object_t` with Emscripten imports, use +/// `wasmer_emscripten_get_emscripten_globals` to get a +/// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +/// +/// WARNING: +///1 +/// This `import_object_t` contains thin-wrappers around host system calls. +/// Do not use this to execute untrusted code without additional sandboxing. +#[no_mangle] +pub unsafe extern "C" fn wasmer_emscripten_generate_import_object( + globals: *mut wasmer_emscripten_globals_t, +) -> *mut wasmer_import_object_t { + if globals.is_null() { + return ptr::null_mut(); + } + // TODO: figure out if we should be using UnsafeCell here or something + let g = &mut *(globals as *mut EmscriptenGlobals); + let import_object = Box::new(wasmer_emscripten::generate_emscripten_env(g)); + + Box::into_raw(import_object) as *mut wasmer_import_object_t +} diff --git a/lib/runtime-c-api/src/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 2bca1e3902c..1c3c1521c8f 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -8,7 +8,7 @@ use crate::{ value::wasmer_value_tag, wasmer_byte_array, wasmer_result_t, }; -use libc::{c_uchar, c_uint}; +use libc::c_uint; use std::{convert::TryFrom, ffi::c_void, ptr, slice, sync::Arc}; use wasmer_runtime::{Global, Memory, Module, Table}; use wasmer_runtime_core::{ @@ -61,6 +61,12 @@ mod wasi; #[cfg(feature = "wasi")] pub use self::wasi::*; +#[cfg(feature = "emscripten")] +mod emscripten; + +#[cfg(feature = "emscripten")] +pub use self::emscripten::*; + /// Gets an entry from an ImportObject at the name and namespace. /// Stores `name`, `namespace`, and `import_export_value` in `import`. /// Thus these must remain valid for the lifetime of `import`. @@ -437,6 +443,9 @@ pub unsafe extern "C" fn wasmer_import_descriptors( module: *const wasmer_module_t, import_descriptors: *mut *mut wasmer_import_descriptors_t, ) { + if module.is_null() { + return; + } let module = &*(module as *const Module); let total_imports = module.info().imported_functions.len() + module.info().imported_tables.len() diff --git a/lib/runtime-c-api/src/import/wasi.rs b/lib/runtime-c-api/src/import/wasi.rs index 0ff833a61f5..1dd9d4c15e9 100644 --- a/lib/runtime-c-api/src/import/wasi.rs +++ b/lib/runtime-c-api/src/import/wasi.rs @@ -1,5 +1,6 @@ use super::*; use crate::get_slice_checked; +use libc::c_uchar; use std::{path::PathBuf, ptr, str}; use wasmer_wasi as wasi; diff --git a/lib/runtime-c-api/tests/.gitignore b/lib/runtime-c-api/tests/.gitignore index cbc7f3b1fcf..b62699da34d 100644 --- a/lib/runtime-c-api/tests/.gitignore +++ b/lib/runtime-c-api/tests/.gitignore @@ -26,4 +26,5 @@ test-module-imports test-module-serialize test-tables test-validate -test-wasi-import-object \ No newline at end of file +test-wasi-import-object +test-emscripten-import-object \ No newline at end of file diff --git a/lib/runtime-c-api/tests/CMakeLists.txt b/lib/runtime-c-api/tests/CMakeLists.txt index 20f7dea13bd..f9aecd4d694 100644 --- a/lib/runtime-c-api/tests/CMakeLists.txt +++ b/lib/runtime-c-api/tests/CMakeLists.txt @@ -22,6 +22,11 @@ if (DEFINED WASI_TESTS) add_executable(test-wasi-import-object test-wasi-import-object.c) endif() +if (DEFINED EMSCRIPTEN_TESTS) + add_executable(test-emscripten-import-object test-emscripten-import-object.c) +endif() + + find_library( WASMER_LIB NAMES libwasmer_runtime_c_api.dylib libwasmer_runtime_c_api.so wasmer_runtime_c_api.dll PATHS ${CMAKE_SOURCE_DIR}/../../../target/release/ @@ -74,6 +79,12 @@ if (DEFINED WASI_TESTS) add_test(test-wasi-import-object test-wasi-import-object) endif() +if (DEFINED EMSCRIPTEN_TESTS) + target_link_libraries(test-emscripten-import-object general ${WASMER_LIB}) + target_compile_options(test-emscripten-import-object PRIVATE ${COMPILER_OPTIONS}) + add_test(test-emscripten-import-object test-emscripten-import-object) +endif() + target_link_libraries(test-instantiate general ${WASMER_LIB}) target_compile_options(test-instantiate PRIVATE ${COMPILER_OPTIONS}) add_test(test-instantiate test-instantiate) diff --git a/lib/runtime-c-api/tests/assets/emscripten_hello_world.c b/lib/runtime-c-api/tests/assets/emscripten_hello_world.c new file mode 100644 index 00000000000..998892f20c9 --- /dev/null +++ b/lib/runtime-c-api/tests/assets/emscripten_hello_world.c @@ -0,0 +1,10 @@ +#include + +int main(int argc, char *argv[]) { + printf("Hello, world\n"); + for ( int i = 0; i < argc; ++i ) { + printf("Arg %d: '%s'\n", i, argv[i]); + } + return 0; +} + diff --git a/lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm b/lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm new file mode 100644 index 00000000000..96100d4e45c Binary files /dev/null and b/lib/runtime-c-api/tests/assets/emscripten_hello_world.wasm differ diff --git a/lib/runtime-c-api/tests/runtime_c_api_tests.rs b/lib/runtime-c-api/tests/runtime_c_api_tests.rs index ed414636374..a5370608274 100644 --- a/lib/runtime-c-api/tests/runtime_c_api_tests.rs +++ b/lib/runtime-c-api/tests/runtime_c_api_tests.rs @@ -8,8 +8,10 @@ fn test_c_api() { ".", #[cfg(feature = "wasi")] "-DWASI_TESTS=ON", + #[cfg(feature = "emscripten")] + "-DEMSCRIPTEN_TESTS=ON", ]; - // we use -f so it doesn't fail if the fiel doesn't exist + // we use -f so it doesn't fail if the file doesn't exist run_command("rm", project_tests_dir, vec!["-f", "CMakeCache.txt"]); run_command("cmake", project_tests_dir, cmake_args); run_command("make", project_tests_dir, vec!["-Wdev", "-Werror=dev"]); diff --git a/lib/runtime-c-api/tests/test-emscripten-import-object.c b/lib/runtime-c-api/tests/test-emscripten-import-object.c new file mode 100644 index 00000000000..bbc581db3e5 --- /dev/null +++ b/lib/runtime-c-api/tests/test-emscripten-import-object.c @@ -0,0 +1,138 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +static bool host_print_called = false; + +// Host function that will be imported into the Web Assembly Instance +void host_print(const wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) +{ + host_print_called = true; + const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0); + uint32_t mem_len = wasmer_memory_length(memory); + uint8_t *mem_bytes = wasmer_memory_data(memory); + printf("%.*s", len, mem_bytes + ptr); +} + +// Use the last_error API to retrieve error messages +void print_wasmer_error() +{ + int error_len = wasmer_last_error_length(); + printf("Error len: `%d`\n", error_len); + char *error_str = malloc(error_len); + wasmer_last_error_message(error_str, error_len); + printf("Error str: `%s`\n", error_str); +} + +// helper function to print byte array to stdout +void print_byte_array(wasmer_byte_array *arr) { + for (int i = 0; i < arr->bytes_len; ++i) { + putchar(arr->bytes[i]); + } +} + +int main() +{ + // Read the Wasm file bytes. + FILE *file = fopen("assets/emscripten_hello_world.wasm", "r"); + assert(file); + fseek(file, 0, SEEK_END); + long len = ftell(file); + uint8_t *bytes = malloc(len); + fseek(file, 0, SEEK_SET); + fread(bytes, 1, len, file); + fclose(file); + + wasmer_module_t *module = NULL; + // Compile the WebAssembly module + wasmer_result_t compile_result = wasmer_compile(&module, bytes, len); + printf("Compile result: %d\n", compile_result); + + if (compile_result != WASMER_OK) + { + print_wasmer_error(); + } + + assert(compile_result == WASMER_OK); + + // Set up data for Emscripten + wasmer_emscripten_globals_t *emscripten_globals = wasmer_emscripten_get_globals(module); + + if (!emscripten_globals) + { + print_wasmer_error(); + } + assert(emscripten_globals); + + // Create the Emscripten import object + wasmer_import_object_t *import_object = + wasmer_emscripten_generate_import_object(emscripten_globals); + + + // Instantiatoe the module with our import_object + wasmer_instance_t *instance = NULL; + wasmer_result_t instantiate_result = wasmer_module_import_instantiate(&instance, module, import_object); + printf("Instantiate result: %d\n", instantiate_result); + + if (instantiate_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(instantiate_result == WASMER_OK); + + // Set up emscripten to be called + wasmer_result_t setup_result = wasmer_emscripten_set_up(instance, emscripten_globals); + printf("Set up result: %d\n", setup_result); + + if (setup_result != WASMER_OK) + { + print_wasmer_error(); + } + assert(setup_result == WASMER_OK); + + + const char *emscripten_prog_name = "emscripten_test_program"; + const char *emscripten_first_arg = "--help"; + wasmer_byte_array args[] = { + { .bytes = (const uint8_t *) emscripten_prog_name, + .bytes_len = strlen(emscripten_prog_name) }, + { .bytes = (const uint8_t *) emscripten_first_arg, + .bytes_len = strlen(emscripten_first_arg) } + }; + int emscripten_argc = sizeof(args) / sizeof(args[0]); + + wasmer_result_t main_result = wasmer_emscripten_call_main(instance, args, emscripten_argc); + + printf("Main result: %d\n", main_result); + assert(main_result == WASMER_OK); + + wasmer_import_object_iter_t *func_iter = wasmer_import_object_iterate_functions(import_object); + + puts("Functions in import object:"); + while ( !wasmer_import_object_iter_at_end(func_iter) ) { + wasmer_import_t import; + wasmer_result_t result = wasmer_import_object_iter_next(func_iter, &import); + assert(result == WASMER_OK); + + print_byte_array(&import.module_name); + putchar(' '); + print_byte_array(&import.import_name); + putchar('\n'); + + assert(import.tag == WASM_FUNCTION); + assert(import.value.func); + wasmer_import_object_imports_destroy(&import, 1); + } + wasmer_import_object_iter_destroy(func_iter); + + // Use *_destroy methods to cleanup as specified in the header documentation + wasmer_emscripten_destroy_globals(emscripten_globals); + wasmer_instance_destroy(instance); + wasmer_import_object_destroy(import_object); + wasmer_module_destroy(module); + + return 0; +} + diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index de921e3b04d..77c888abe6e 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -14,6 +14,7 @@ #endif #endif +#define WASMER_EMSCRIPTEN_ENABLED #endif // WASMER_H_MACROS @@ -74,18 +75,35 @@ typedef struct { } wasmer_module_t; -/** - * Opaque pointer to `NamedExportDescriptor`. - */ typedef struct { -} wasmer_export_descriptor_t; +} wasmer_instance_t; typedef struct { const uint8_t *bytes; uint32_t bytes_len; } wasmer_byte_array; +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Type used to construct an import_object_t with Emscripten imports. + */ +typedef struct { + +} wasmer_emscripten_globals_t; +#endif + +typedef struct { + +} wasmer_import_object_t; + +/** + * Opaque pointer to `NamedExportDescriptor`. + */ +typedef struct { + +} wasmer_export_descriptor_t; + /** * Opaque pointer to `NamedExportDescriptors`. */ @@ -153,10 +171,6 @@ typedef struct { typedef struct { -} wasmer_import_object_t; - -typedef struct { - } wasmer_table_t; /** @@ -182,10 +196,6 @@ typedef struct { typedef struct { -} wasmer_instance_t; - -typedef struct { - } wasmer_instance_context_t; typedef struct { @@ -247,6 +257,64 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Convenience function for setting up arguments and calling the Emscripten + * main function. + * + * WARNING: + * + * Do not call this function on untrusted code when operating without + * additional sandboxing in place. + * Emscripten has access to many host system calls and therefore may do very + * bad things. + */ +wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, + const wasmer_byte_array *args, + unsigned int args_len); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Destroy `wasmer_emscrpten_globals_t` created by + * `wasmer_emscripten_get_emscripten_globals`. + */ +void wasmer_emscripten_destroy_globals(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Create a `wasmer_import_object_t` with Emscripten imports, use + * `wasmer_emscripten_get_emscripten_globals` to get a + * `wasmer_emscripten_globals_t` from a `wasmer_module_t`. + * + * WARNING: + *1 + * This `import_object_t` contains thin-wrappers around host system calls. + * Do not use this to execute untrusted code without additional sandboxing. + */ +wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Create a `wasmer_emscripten_globals_t` from a Wasm module. + */ +wasmer_emscripten_globals_t *wasmer_emscripten_get_globals(const wasmer_module_t *module); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/** + * Execute global constructors (required if the module is compiled from C++) + * and sets up the internal environment. + * + * This function sets the data pointer in the same way that + * [`wasmer_instance_context_data_set`] does. + */ +wasmer_result_t wasmer_emscripten_set_up(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); +#endif + /** * Gets export descriptor kind */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 09b58f86100..04c2168afac 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -14,6 +14,7 @@ #endif #endif +#define WASMER_EMSCRIPTEN_ENABLED #endif // WASMER_H_MACROS @@ -61,8 +62,7 @@ struct wasmer_module_t { }; -/// Opaque pointer to `NamedExportDescriptor`. -struct wasmer_export_descriptor_t { +struct wasmer_instance_t { }; @@ -71,6 +71,22 @@ struct wasmer_byte_array { uint32_t bytes_len; }; +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Type used to construct an import_object_t with Emscripten imports. +struct wasmer_emscripten_globals_t { + +}; +#endif + +struct wasmer_import_object_t { + +}; + +/// Opaque pointer to `NamedExportDescriptor`. +struct wasmer_export_descriptor_t { + +}; + /// Opaque pointer to `NamedExportDescriptors`. struct wasmer_export_descriptors_t { @@ -128,10 +144,6 @@ struct wasmer_import_func_t { }; -struct wasmer_import_object_t { - -}; - struct wasmer_table_t { }; @@ -155,10 +167,6 @@ struct wasmer_import_object_iter_t { }; -struct wasmer_instance_t { - -}; - struct wasmer_instance_context_t { }; @@ -216,6 +224,54 @@ wasmer_result_t wasmer_compile(wasmer_module_t **module, uint8_t *wasm_bytes, uint32_t wasm_bytes_len); +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Convenience function for setting up arguments and calling the Emscripten +/// main function. +/// +/// WARNING: +/// +/// Do not call this function on untrusted code when operating without +/// additional sandboxing in place. +/// Emscripten has access to many host system calls and therefore may do very +/// bad things. +wasmer_result_t wasmer_emscripten_call_main(wasmer_instance_t *instance, + const wasmer_byte_array *args, + unsigned int args_len); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Destroy `wasmer_emscrpten_globals_t` created by +/// `wasmer_emscripten_get_emscripten_globals`. +void wasmer_emscripten_destroy_globals(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Create a `wasmer_import_object_t` with Emscripten imports, use +/// `wasmer_emscripten_get_emscripten_globals` to get a +/// `wasmer_emscripten_globals_t` from a `wasmer_module_t`. +/// +/// WARNING: +///1 +/// This `import_object_t` contains thin-wrappers around host system calls. +/// Do not use this to execute untrusted code without additional sandboxing. +wasmer_import_object_t *wasmer_emscripten_generate_import_object(wasmer_emscripten_globals_t *globals); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Create a `wasmer_emscripten_globals_t` from a Wasm module. +wasmer_emscripten_globals_t *wasmer_emscripten_get_globals(const wasmer_module_t *module); +#endif + +#if defined(WASMER_EMSCRIPTEN_ENABLED) +/// Execute global constructors (required if the module is compiled from C++) +/// and sets up the internal environment. +/// +/// This function sets the data pointer in the same way that +/// [`wasmer_instance_context_data_set`] does. +wasmer_result_t wasmer_emscripten_set_up(wasmer_instance_t *instance, + wasmer_emscripten_globals_t *globals); +#endif + /// Gets export descriptor kind wasmer_import_export_kind wasmer_export_descriptor_kind(wasmer_export_descriptor_t *export_);