From ae19e7f71b5ca6bd9d79bdfbab61d1a5845d3e22 Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 21:23:40 +0800 Subject: [PATCH 1/5] Trampoline for calling with context. --- lib/runtime-core/src/lib.rs | 7 ++ lib/runtime-core/src/trampoline_x64.rs | 128 +++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 lib/runtime-core/src/trampoline_x64.rs diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 20708c17a5e..8dc0babcb14 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -8,6 +8,9 @@ extern crate field_offset; #[macro_use] extern crate serde_derive; +#[macro_use] +extern crate lazy_static; + #[macro_use] mod macros; #[doc(hidden)] @@ -35,6 +38,10 @@ pub mod units; pub mod vm; #[doc(hidden)] pub mod vmcalls; +#[cfg(all(unix, target_arch = "x86_64"))] +pub mod trampoline_x64; +#[cfg(all(unix, target_arch = "x86_64"))] +pub use trampoline_x64 as trampoline; use self::error::CompileResult; #[doc(inline)] diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs new file mode 100644 index 00000000000..ab83f1605b5 --- /dev/null +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -0,0 +1,128 @@ +//! Trampoline generator for carrying context with function pointer. +//! +//! This makes use of the `mm0` register to pass the context as an implicit "parameter" because `mm0` is +//! not used to pass parameters and is almost never used by modern compilers. It's still better to call +//! `get_context()` as early as possible in the callee function though, as a good practice. +//! +//! Variadic functions are not supported because `rax` is used by the trampoline code. + +use crate::loader::CodeMemory; + +lazy_static! { + static ref GET_CONTEXT: extern "C" fn () -> *const CallContext = { + static CODE: &'static [u8] = &[ + 0x48, 0x0f, 0x7e, 0xc0, // movq %mm0, %rax + 0xc3, // retq + ]; + let mut mem = CodeMemory::new(4096); + mem[..CODE.len()].copy_from_slice(CODE); + mem.make_executable(); + let ptr = mem.as_ptr(); + ::std::mem::forget(mem); + unsafe { + ::std::mem::transmute(ptr) + } + }; +} + +pub enum CallTarget {} +pub enum CallContext {} +pub enum Trampoline {} + +pub struct TrampolineBufferBuilder { + code: Vec, + offsets: Vec, +} + +pub struct TrampolineBuffer { + code: CodeMemory, + offsets: Vec, +} + +fn pointer_to_bytes(ptr: &*const T) -> &[u8] { + unsafe { + ::std::slice::from_raw_parts( + ptr as *const *const T as *const u8, + ::std::mem::size_of::<*const T>(), + ) + } +} + +pub fn get_context() -> *const CallContext { + GET_CONTEXT() +} + +impl TrampolineBufferBuilder { + pub fn new() -> TrampolineBufferBuilder { + TrampolineBufferBuilder { + code: vec![], + offsets: vec![], + } + } + + pub fn add_function(&mut self, target: *const CallTarget, context: *const CallContext) -> usize { + let idx = self.offsets.len(); + self.offsets.push(self.code.len()); + self.code.extend_from_slice(&[ + 0x48, 0xb8, // movabsq ?, %rax + ]); + self.code.extend_from_slice(pointer_to_bytes(&context)); + self.code.extend_from_slice(&[ + 0x48, 0x0f, 0x6e, 0xc0, // movq %rax, %mm0 + ]); + self.code.extend_from_slice(&[ + 0x48, 0xb8, // movabsq ?, %rax + ]); + self.code.extend_from_slice(pointer_to_bytes(&target)); + self.code.extend_from_slice(&[ + 0xff, 0xe0, // jmpq *%rax + ]); + idx + } + + pub fn build(self) -> TrampolineBuffer { + get_context(); // ensure lazy initialization is completed + + let mut code = CodeMemory::new(self.code.len()); + code[..self.code.len()].copy_from_slice(&self.code); + code.make_executable(); + TrampolineBuffer { + code, + offsets: self.offsets, + } + } +} + +impl TrampolineBuffer { + pub fn get_trampoline(&self, idx: usize) -> *const Trampoline { + &self.code[self.offsets[idx]] as *const u8 as *const Trampoline + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_trampoline_call() { + struct TestContext { + value: i32, + } + extern "C" fn do_add(a: i32, b: f32) -> f32 { + let ctx = unsafe { + &*(get_context() as *const TestContext) + }; + a as f32 + b + ctx.value as f32 + } + let mut builder = TrampolineBufferBuilder::new(); + let ctx = TestContext { + value: 3, + }; + let idx = builder.add_function(do_add as usize as *const _, &ctx as *const TestContext as *const _); + let buf = builder.build(); + let t = buf.get_trampoline(idx); + let ret = unsafe { + ::std::mem::transmute::<_, extern "C" fn (i32, f32) -> f32>(t)(1, 2.0) as i32 + }; + assert_eq!(ret, 6); + } +} From 7808c68cb2e9a52b4c0f73a1801baa5fb54c0fb3 Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 21:24:41 +0800 Subject: [PATCH 2/5] Cargo fmt --- lib/runtime-core/src/lib.rs | 4 ++-- lib/runtime-core/src/trampoline_x64.rs | 28 ++++++++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 8dc0babcb14..026b76eeb0e 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -32,6 +32,8 @@ mod sig_registry; pub mod structures; mod sys; pub mod table; +#[cfg(all(unix, target_arch = "x86_64"))] +pub mod trampoline_x64; pub mod typed_func; pub mod types; pub mod units; @@ -39,8 +41,6 @@ pub mod vm; #[doc(hidden)] pub mod vmcalls; #[cfg(all(unix, target_arch = "x86_64"))] -pub mod trampoline_x64; -#[cfg(all(unix, target_arch = "x86_64"))] pub use trampoline_x64 as trampoline; use self::error::CompileResult; diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index ab83f1605b5..251a1231b0c 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -1,9 +1,9 @@ //! Trampoline generator for carrying context with function pointer. -//! +//! //! This makes use of the `mm0` register to pass the context as an implicit "parameter" because `mm0` is //! not used to pass parameters and is almost never used by modern compilers. It's still better to call //! `get_context()` as early as possible in the callee function though, as a good practice. -//! +//! //! Variadic functions are not supported because `rax` is used by the trampoline code. use crate::loader::CodeMemory; @@ -60,7 +60,11 @@ impl TrampolineBufferBuilder { } } - pub fn add_function(&mut self, target: *const CallTarget, context: *const CallContext) -> usize { + pub fn add_function( + &mut self, + target: *const CallTarget, + context: *const CallContext, + ) -> usize { let idx = self.offsets.len(); self.offsets.push(self.code.len()); self.code.extend_from_slice(&[ @@ -108,21 +112,19 @@ mod tests { value: i32, } extern "C" fn do_add(a: i32, b: f32) -> f32 { - let ctx = unsafe { - &*(get_context() as *const TestContext) - }; + let ctx = unsafe { &*(get_context() as *const TestContext) }; a as f32 + b + ctx.value as f32 } let mut builder = TrampolineBufferBuilder::new(); - let ctx = TestContext { - value: 3, - }; - let idx = builder.add_function(do_add as usize as *const _, &ctx as *const TestContext as *const _); + let ctx = TestContext { value: 3 }; + let idx = builder.add_function( + do_add as usize as *const _, + &ctx as *const TestContext as *const _, + ); let buf = builder.build(); let t = buf.get_trampoline(idx); - let ret = unsafe { - ::std::mem::transmute::<_, extern "C" fn (i32, f32) -> f32>(t)(1, 2.0) as i32 - }; + let ret = + unsafe { ::std::mem::transmute::<_, extern "C" fn(i32, f32) -> f32>(t)(1, 2.0) as i32 }; assert_eq!(ret, 6); } } From d70cb9695eef50b722b911545d7dc301972f7a00 Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 21:59:33 +0800 Subject: [PATCH 3/5] Add trampoline-related functions to C API. --- lib/runtime-c-api/src/lib.rs | 2 + lib/runtime-c-api/src/trampoline.rs | 54 +++++++++++++++++++ .../tests/test-import-function.c | 25 ++++++++- lib/runtime-c-api/wasmer.h | 27 ++++++++++ lib/runtime-c-api/wasmer.hh | 27 ++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 lib/runtime-c-api/src/trampoline.rs diff --git a/lib/runtime-c-api/src/lib.rs b/lib/runtime-c-api/src/lib.rs index 054cd14e1d3..fb95c7dc622 100644 --- a/lib/runtime-c-api/src/lib.rs +++ b/lib/runtime-c-api/src/lib.rs @@ -95,6 +95,8 @@ pub mod instance; pub mod memory; pub mod module; pub mod table; +#[cfg(all(unix, target_arch = "x86_64"))] +pub mod trampoline; pub mod value; #[allow(non_camel_case_types)] diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs new file mode 100644 index 00000000000..e6429f71b16 --- /dev/null +++ b/lib/runtime-c-api/src/trampoline.rs @@ -0,0 +1,54 @@ +use std::ffi::c_void; +use wasmer_runtime_core::trampoline::*; + +#[repr(C)] +pub struct wasmer_trampoline_buffer_builder_t; + +#[repr(C)] +pub struct wasmer_trampoline_buffer_t; + +#[repr(C)] +pub struct wasmer_trampoline_callable_t; + +#[no_mangle] +pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoline_buffer_builder_t +{ + Box::into_raw(Box::new(TrampolineBufferBuilder::new())) as *mut _ +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_function( + b: *mut wasmer_trampoline_buffer_builder_t, + f: *const wasmer_trampoline_callable_t, + ctx: *const c_void, +) -> usize { + let b = &mut *(b as *mut TrampolineBufferBuilder); + b.add_function(f as *const CallTarget, ctx as *const CallContext) +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build( + b: *mut wasmer_trampoline_buffer_builder_t, +) -> *mut wasmer_trampoline_buffer_t { + let b = Box::from_raw(b as *mut TrampolineBufferBuilder); + Box::into_raw(Box::new(b.build())) as *mut _ +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(b: *mut wasmer_trampoline_buffer_t) { + Box::from_raw(b); +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline( + b: *const wasmer_trampoline_buffer_t, + idx: usize, +) -> *const wasmer_trampoline_callable_t { + let b = &*(b as *const TrampolineBuffer); + b.get_trampoline(idx) as _ +} + +#[no_mangle] +pub unsafe extern "C" fn wasmer_trampoline_get_context() -> *mut c_void { + get_context() as *const c_void as *mut c_void +} diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c index 92e3e821db7..3447fcdddc5 100644 --- a/lib/runtime-c-api/tests/test-import-function.c +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -14,8 +14,15 @@ typedef struct { int value; } context_data; +struct print_str_context { + int call_count; +}; + void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len) { + struct print_str_context *local_context = wasmer_trampoline_get_context(); + local_context->call_count++; + 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); @@ -36,9 +43,22 @@ int main() { wasmer_value_tag params_sig[] = {WASM_I32, WASM_I32}; wasmer_value_tag returns_sig[] = {}; + struct print_str_context local_context = { + .call_count = 0 + }; + + printf("Creating trampoline buffer\n"); + wasmer_trampoline_buffer_builder_t *tbb = wasmer_trampoline_buffer_builder_new(); + unsigned long print_str_idx = wasmer_trampoline_buffer_builder_add_function( + tbb, + (wasmer_trampoline_callable_t *) print_str, + (void *) &local_context + ); + wasmer_trampoline_buffer_t *tb = wasmer_trampoline_buffer_builder_build(tbb); + const wasmer_trampoline_callable_t *print_str_callable = wasmer_trampoline_buffer_get_trampoline(tb, print_str_idx); printf("Creating new func\n"); - wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str, params_sig, 2, returns_sig, 0); + wasmer_import_func_t *func = wasmer_import_func_new((void (*)(void *)) print_str_callable, params_sig, 2, returns_sig, 0); wasmer_import_t import; char *module_name = "env"; @@ -95,7 +115,10 @@ int main() assert(ptr_len == 13); assert(0 == strcmp(actual_str, "Hello, World!")); assert(context_data_value == actual_context_data_value); + assert(local_context.call_count == 1); + printf("Destroying trampoline buffer\n"); + wasmer_trampoline_buffer_destroy(tb); printf("Destroying func\n"); wasmer_import_func_destroy(func); printf("Destroy instance\n"); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 6752f71a1c7..1b04b0a7396 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -133,6 +133,18 @@ typedef struct { } wasmer_serialized_module_t; +typedef struct { + +} wasmer_trampoline_buffer_builder_t; + +typedef struct { + +} wasmer_trampoline_callable_t; + +typedef struct { + +} wasmer_trampoline_buffer_t; + /** * Creates a new Module from the given wasm bytes. * Returns `wasmer_result_t::WASMER_OK` upon success. @@ -584,6 +596,21 @@ uint32_t wasmer_table_length(wasmer_table_t *table); */ wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); +uintptr_t wasmer_trampoline_buffer_builder_add_function(wasmer_trampoline_buffer_builder_t *b, + const wasmer_trampoline_callable_t *f, + const void *ctx); + +wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *b); + +wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(void); + +void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *b); + +const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *b, + uintptr_t idx); + +void *wasmer_trampoline_get_context(void); + /** * Returns true for valid wasm bytes and false for invalid bytes */ diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 99e21fcc8de..373ccb0742e 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -131,6 +131,18 @@ struct wasmer_serialized_module_t { }; +struct wasmer_trampoline_buffer_builder_t { + +}; + +struct wasmer_trampoline_callable_t { + +}; + +struct wasmer_trampoline_buffer_t { + +}; + extern "C" { /// Creates a new Module from the given wasm bytes. @@ -458,6 +470,21 @@ uint32_t wasmer_table_length(wasmer_table_t *table); /// and `wasmer_last_error_message` to get an error message. wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits); +uintptr_t wasmer_trampoline_buffer_builder_add_function(wasmer_trampoline_buffer_builder_t *b, + const wasmer_trampoline_callable_t *f, + const void *ctx); + +wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *b); + +wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(); + +void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *b); + +const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *b, + uintptr_t idx); + +void *wasmer_trampoline_get_context(); + /// Returns true for valid wasm bytes and false for invalid bytes bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len); From 0a44add31cba712b046141b9e43fb0d291db3b70 Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 22:32:35 +0800 Subject: [PATCH 4/5] Fix clippy errors. --- lib/runtime-c-api/src/trampoline.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index e6429f71b16..93c91a780d2 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -11,12 +11,14 @@ pub struct wasmer_trampoline_buffer_t; pub struct wasmer_trampoline_callable_t; #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoline_buffer_builder_t { Box::into_raw(Box::new(TrampolineBufferBuilder::new())) as *mut _ } #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_function( b: *mut wasmer_trampoline_buffer_builder_t, f: *const wasmer_trampoline_callable_t, @@ -27,6 +29,7 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_function( } #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build( b: *mut wasmer_trampoline_buffer_builder_t, ) -> *mut wasmer_trampoline_buffer_t { @@ -35,11 +38,13 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build( } #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(b: *mut wasmer_trampoline_buffer_t) { Box::from_raw(b); } #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline( b: *const wasmer_trampoline_buffer_t, idx: usize, @@ -49,6 +54,7 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline( } #[no_mangle] +#[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_get_context() -> *mut c_void { get_context() as *const c_void as *mut c_void } From 06280e225e318cc5890df1f9b3eae9305f5b0aad Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 22:42:45 +0800 Subject: [PATCH 5/5] Fix unused_imports error on lazy_static. --- lib/runtime-core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/runtime-core/src/lib.rs b/lib/runtime-core/src/lib.rs index 026b76eeb0e..662f5c970fd 100644 --- a/lib/runtime-core/src/lib.rs +++ b/lib/runtime-core/src/lib.rs @@ -8,6 +8,7 @@ extern crate field_offset; #[macro_use] extern crate serde_derive; +#[allow(unused_imports)] #[macro_use] extern crate lazy_static;