From ae19e7f71b5ca6bd9d79bdfbab61d1a5845d3e22 Mon Sep 17 00:00:00 2001 From: losfair Date: Mon, 3 Jun 2019 21:23:40 +0800 Subject: [PATCH 01/11] 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 20708c17a..8dc0babcb 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 000000000..ab83f1605 --- /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 02/11] 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 8dc0babcb..026b76eeb 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 ab83f1605..251a1231b 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 03/11] 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 054cd14e1..fb95c7dc6 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 000000000..e6429f71b --- /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 92e3e821d..3447fcddd 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 6752f71a1..1b04b0a73 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 99e21fcc8..373ccb074 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 04/11] 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 e6429f71b..93c91a780 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 05/11] 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 026b76eeb..662f5c970 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; From 8a74399c6ee998ba5dbcf768f298f3887751fbef Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:11:10 +0800 Subject: [PATCH 06/11] Add callinfo trampoline support. --- lib/runtime-core/src/trampoline_x64.rs | 113 +++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 8 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 251a1231b..68fab2693 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -39,11 +39,11 @@ pub struct TrampolineBuffer { offsets: Vec, } -fn pointer_to_bytes(ptr: &*const T) -> &[u8] { +fn value_to_bytes(ptr: &T) -> &[u8] { unsafe { ::std::slice::from_raw_parts( - ptr as *const *const T as *const u8, - ::std::mem::size_of::<*const T>(), + ptr as *const T as *const u8, + ::std::mem::size_of::(), ) } } @@ -60,7 +60,7 @@ impl TrampolineBufferBuilder { } } - pub fn add_function( + pub fn add_context_trampoline( &mut self, target: *const CallTarget, context: *const CallContext, @@ -70,20 +70,94 @@ impl TrampolineBufferBuilder { self.code.extend_from_slice(&[ 0x48, 0xb8, // movabsq ?, %rax ]); - self.code.extend_from_slice(pointer_to_bytes(&context)); + self.code.extend_from_slice(value_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(value_to_bytes(&target)); self.code.extend_from_slice(&[ 0xff, 0xe0, // jmpq *%rax ]); idx } + pub fn add_callinfo_trampoline( + &mut self, + target: unsafe extern "C" fn (*const CallContext, *const u64) -> u64, + context: *const CallContext, + num_params: u32, + ) -> usize { + let idx = self.offsets.len(); + self.offsets.push(self.code.len()); + + let mut stack_offset: u32 = num_params.checked_mul(8).unwrap(); + if stack_offset % 16 == 0 { + stack_offset += 8; + } + + self.code.extend_from_slice(&[ + 0x48, 0x81, 0xec, + ]); // sub ?, %rsp + self.code.extend_from_slice(value_to_bytes(&stack_offset)); + for i in 0..num_params { + match i { + 0..=5 => { + // mov %?, ?(%rsp) + let prefix: &[u8] = match i { + 0 => &[0x48, 0x89, 0xbc, 0x24], // rdi + 1 => &[0x48, 0x89, 0xb4, 0x24], // rsi + 2 => &[0x48, 0x89, 0x94, 0x24], // rdx + 3 => &[0x48, 0x89, 0x8c, 0x24], // rcx + 4 => &[0x4c, 0x89, 0x84, 0x24], // r8 + 5 => &[0x4c, 0x89, 0x8c, 0x24], // r9 + _ => unreachable!(), + }; + self.code.extend_from_slice(prefix); + self.code.extend_from_slice(value_to_bytes(&(i * 8u32))); + } + _ => { + self.code.extend_from_slice(&[ + 0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax + ]); + self.code.extend_from_slice(value_to_bytes(&( + (i - 6) * 8u32 + stack_offset + 8 /* ret addr */ + ))); + // mov %rax, ?(%rsp) + self.code.extend_from_slice(&[ + 0x48, 0x89, 0x84, 0x24, + ]); + self.code.extend_from_slice(value_to_bytes(&(i * 8u32))); + } + } + } + self.code.extend_from_slice(&[ + 0x48, 0xbf, // movabsq ?, %rdi + ]); + self.code.extend_from_slice(value_to_bytes(&context)); + self.code.extend_from_slice(&[ + 0x48, 0x89, 0xe6, // mov %rsp, %rsi + ]); + + self.code.extend_from_slice(&[ + 0x48, 0xb8, // movabsq ?, %rax + ]); + self.code.extend_from_slice(value_to_bytes(&target)); + self.code.extend_from_slice(&[ + 0xff, 0xd0, // callq *%rax + ]); + self.code.extend_from_slice(&[ + 0x48, 0x81, 0xc4, // add ?, %rsp + ]); + self.code.extend_from_slice(value_to_bytes(&stack_offset)); + self.code.extend_from_slice(&[ + 0xc3, //retq + ]); + idx + } + pub fn build(self) -> TrampolineBuffer { get_context(); // ensure lazy initialization is completed @@ -107,7 +181,7 @@ impl TrampolineBuffer { mod tests { use super::*; #[test] - fn test_trampoline_call() { + fn test_context_trampoline() { struct TestContext { value: i32, } @@ -117,7 +191,7 @@ mod tests { } let mut builder = TrampolineBufferBuilder::new(); let ctx = TestContext { value: 3 }; - let idx = builder.add_function( + let idx = builder.add_context_trampoline( do_add as usize as *const _, &ctx as *const TestContext as *const _, ); @@ -127,4 +201,27 @@ mod tests { unsafe { ::std::mem::transmute::<_, extern "C" fn(i32, f32) -> f32>(t)(1, 2.0) as i32 }; assert_eq!(ret, 6); } + #[test] + fn test_callinfo_trampoline() { + struct TestContext { + value: i32, + } + unsafe extern "C" fn do_add(ctx: *const CallContext, args: *const u64) -> u64 { + let ctx = &*(ctx as *const TestContext); + let args: &[u64] = ::std::slice::from_raw_parts(args, 8); + (args.iter().map(|x| *x as i32).fold(0, |a, b| a + b) + ctx.value) as u64 + } + let mut builder = TrampolineBufferBuilder::new(); + let ctx = TestContext { value: 100 }; + let idx = builder.add_callinfo_trampoline( + do_add, + &ctx as *const TestContext as *const _, + 8, + ); + let buf = builder.build(); + let t = buf.get_trampoline(idx); + let ret = + unsafe { ::std::mem::transmute::<_, extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32) -> i32>(t)(1, 2, 3, 4, 5, 6, 7, 8) as i32 }; + assert_eq!(ret, 136); + } } From b2869e181c1caf0fb214bbcac2da2717fbe4ee89 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:11:54 +0800 Subject: [PATCH 07/11] Cargo fmt --- lib/runtime-core/src/trampoline_x64.rs | 35 +++++++++++--------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 68fab2693..83dbf355e 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -41,10 +41,7 @@ pub struct TrampolineBuffer { fn value_to_bytes(ptr: &T) -> &[u8] { unsafe { - ::std::slice::from_raw_parts( - ptr as *const T as *const u8, - ::std::mem::size_of::(), - ) + ::std::slice::from_raw_parts(ptr as *const T as *const u8, ::std::mem::size_of::()) } } @@ -86,7 +83,7 @@ impl TrampolineBufferBuilder { pub fn add_callinfo_trampoline( &mut self, - target: unsafe extern "C" fn (*const CallContext, *const u64) -> u64, + target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64, context: *const CallContext, num_params: u32, ) -> usize { @@ -98,9 +95,7 @@ impl TrampolineBufferBuilder { stack_offset += 8; } - self.code.extend_from_slice(&[ - 0x48, 0x81, 0xec, - ]); // sub ?, %rsp + self.code.extend_from_slice(&[0x48, 0x81, 0xec]); // sub ?, %rsp self.code.extend_from_slice(value_to_bytes(&stack_offset)); for i in 0..num_params { match i { @@ -122,13 +117,11 @@ impl TrampolineBufferBuilder { self.code.extend_from_slice(&[ 0x48, 0x8b, 0x84, 0x24, // mov ?(%rsp), %rax ]); - self.code.extend_from_slice(value_to_bytes(&( - (i - 6) * 8u32 + stack_offset + 8 /* ret addr */ - ))); + self.code.extend_from_slice(value_to_bytes( + &((i - 6) * 8u32 + stack_offset + 8/* ret addr */), + )); // mov %rax, ?(%rsp) - self.code.extend_from_slice(&[ - 0x48, 0x89, 0x84, 0x24, - ]); + self.code.extend_from_slice(&[0x48, 0x89, 0x84, 0x24]); self.code.extend_from_slice(value_to_bytes(&(i * 8u32))); } } @@ -213,15 +206,15 @@ mod tests { } let mut builder = TrampolineBufferBuilder::new(); let ctx = TestContext { value: 100 }; - let idx = builder.add_callinfo_trampoline( - do_add, - &ctx as *const TestContext as *const _, - 8, - ); + let idx = + builder.add_callinfo_trampoline(do_add, &ctx as *const TestContext as *const _, 8); let buf = builder.build(); let t = buf.get_trampoline(idx); - let ret = - unsafe { ::std::mem::transmute::<_, extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32) -> i32>(t)(1, 2, 3, 4, 5, 6, 7, 8) as i32 }; + let ret = unsafe { + ::std::mem::transmute::<_, extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32) -> i32>( + t, + )(1, 2, 3, 4, 5, 6, 7, 8) as i32 + }; assert_eq!(ret, 136); } } From 669f76025ce7ba583a1e5d4ec545e58fea7aafc8 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:25:37 +0800 Subject: [PATCH 08/11] Add callinfo trampoline into the C API. --- lib/runtime-c-api/src/trampoline.rs | 16 ++- .../tests/test-import-function-callinfo.c | 132 ++++++++++++++++++ .../tests/test-import-function.c | 4 +- lib/runtime-c-api/wasmer.h | 11 +- lib/runtime-c-api/wasmer.hh | 11 +- 5 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 lib/runtime-c-api/tests/test-import-function-callinfo.c diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index 93c91a780..a625ad7f2 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -19,13 +19,25 @@ pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoli #[no_mangle] #[allow(clippy::cast_ptr_alignment)] -pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_function( +pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline( 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) + b.add_context_trampoline(f as *const CallTarget, ctx as *const CallContext) +} + +#[no_mangle] +#[allow(clippy::cast_ptr_alignment)] +pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline( + b: *mut wasmer_trampoline_buffer_builder_t, + f: *const wasmer_trampoline_callable_t, + ctx: *const c_void, + num_params: u32, +) -> usize { + let b = &mut *(b as *mut TrampolineBufferBuilder); + b.add_callinfo_trampoline(::std::mem::transmute(f), ctx as *const CallContext, num_params) } #[no_mangle] diff --git a/lib/runtime-c-api/tests/test-import-function-callinfo.c b/lib/runtime-c-api/tests/test-import-function-callinfo.c new file mode 100644 index 000000000..371f9eeba --- /dev/null +++ b/lib/runtime-c-api/tests/test-import-function-callinfo.c @@ -0,0 +1,132 @@ +#include +#include "../wasmer.h" +#include +#include +#include + +static bool print_str_called = false; +static int memory_len = 0; +static int ptr_len = 0; +static char actual_str[14] = {}; +static int actual_context_data_value = 0; + +typedef struct { + int value; +} context_data; + +struct print_str_context { + int call_count; +}; + +void print_str(struct print_str_context *local_context, uint64_t *args) +{ + local_context->call_count++; + + wasmer_instance_context_t *ctx = (void *) args[0]; + int32_t ptr = args[1]; + int32_t len = args[2]; + + 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); + for (int32_t idx = 0; idx < len; idx++) + { + actual_str[idx] = mem_bytes[ptr + idx]; + } + actual_str[13] = '\0'; + printf("In print_str, memory len: %d, ptr_len: %d\n, str %s", mem_len, len, actual_str); + print_str_called = true; + memory_len = mem_len; + ptr_len = len; + + actual_context_data_value = ((context_data *) wasmer_instance_context_data_get(ctx))->value; +} + +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_callinfo_trampoline( + tbb, + (wasmer_trampoline_callable_t *) print_str, + (void *) &local_context, + 3 + ); + 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_callable, params_sig, 2, returns_sig, 0); + wasmer_import_t import; + + char *module_name = "env"; + wasmer_byte_array module_name_bytes; + module_name_bytes.bytes = (const uint8_t *) module_name; + module_name_bytes.bytes_len = strlen(module_name); + char *import_name = "print_str"; + wasmer_byte_array import_name_bytes; + import_name_bytes.bytes = (const uint8_t *) import_name; + import_name_bytes.bytes_len = strlen(import_name); + + import.module_name = module_name_bytes; + import.import_name = import_name_bytes; + import.tag = WASM_FUNCTION; + import.value.func = func; + wasmer_import_t imports[] = {import}; + + // Read the wasm file bytes + FILE *file = fopen("assets/wasm_sample_app.wasm", "r"); + 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); + + printf("Instantiating\n"); + wasmer_instance_t *instance = NULL; + wasmer_result_t compile_result = wasmer_instantiate(&instance, bytes, len, imports, 1); + printf("Compile result: %d\n", compile_result); + + assert(compile_result == WASMER_OK); + + context_data* context_data = malloc(sizeof(context_data)); + int context_data_value = 42; + context_data->value = context_data_value; + wasmer_instance_context_data_set(instance, context_data); + + wasmer_value_t params[] = {}; + wasmer_value_t results[] = {}; + wasmer_result_t call_result = wasmer_instance_call(instance, "hello_wasm", params, 0, results, 0); + printf("Call result: %d\n", call_result); + + 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); + + assert(call_result == WASMER_OK); + + assert(print_str_called); + assert(memory_len == 17); + 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"); + wasmer_instance_destroy(instance); + free(context_data); + return 0; +} diff --git a/lib/runtime-c-api/tests/test-import-function.c b/lib/runtime-c-api/tests/test-import-function.c index 3447fcddd..a6bea2a54 100644 --- a/lib/runtime-c-api/tests/test-import-function.c +++ b/lib/runtime-c-api/tests/test-import-function.c @@ -22,7 +22,7 @@ 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); @@ -49,7 +49,7 @@ int main() 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( + unsigned long print_str_idx = wasmer_trampoline_buffer_builder_add_context_trampoline( tbb, (wasmer_trampoline_callable_t *) print_str, (void *) &local_context diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 1b04b0a73..e03b6fb58 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -596,9 +596,14 @@ 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); +uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *b, + const wasmer_trampoline_callable_t *f, + const void *ctx, + uint32_t num_params); + +uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(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); diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 373ccb074..7e3c6612f 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -470,9 +470,14 @@ 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); +uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *b, + const wasmer_trampoline_callable_t *f, + const void *ctx, + uint32_t num_params); + +uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(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); From f1b27d57740299818f364e14e0949cbbdaaf44bf Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:26:35 +0800 Subject: [PATCH 09/11] Cargo fmt --- lib/runtime-c-api/src/trampoline.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index a625ad7f2..d9b34f9ce 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -37,7 +37,11 @@ pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampolin num_params: u32, ) -> usize { let b = &mut *(b as *mut TrampolineBufferBuilder); - b.add_callinfo_trampoline(::std::mem::transmute(f), ctx as *const CallContext, num_params) + b.add_callinfo_trampoline( + ::std::mem::transmute(f), + ctx as *const CallContext, + num_params, + ) } #[no_mangle] From f4df568e414f9425e670b695e5cdfe786f6ce1f2 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:38:35 +0800 Subject: [PATCH 10/11] Naming fixes and documentation for trampoline API. --- lib/runtime-c-api/src/trampoline.rs | 48 +++++++++++++++++------------ lib/runtime-c-api/wasmer.h | 35 ++++++++++++++++----- lib/runtime-c-api/wasmer.hh | 21 ++++++++----- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/lib/runtime-c-api/src/trampoline.rs b/lib/runtime-c-api/src/trampoline.rs index d9b34f9ce..923622aa5 100644 --- a/lib/runtime-c-api/src/trampoline.rs +++ b/lib/runtime-c-api/src/trampoline.rs @@ -1,4 +1,7 @@ +//! Trampoline emitter for transforming function calls. + use std::ffi::c_void; +use std::mem; use wasmer_runtime_core::trampoline::*; #[repr(C)] @@ -10,6 +13,7 @@ pub struct wasmer_trampoline_buffer_t; #[repr(C)] pub struct wasmer_trampoline_callable_t; +/// Creates a new trampoline builder. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoline_buffer_builder_t @@ -17,58 +21,62 @@ pub extern "C" fn wasmer_trampoline_buffer_builder_new() -> *mut wasmer_trampoli Box::into_raw(Box::new(TrampolineBufferBuilder::new())) as *mut _ } +/// Adds a context trampoline to the builder. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_context_trampoline( - b: *mut wasmer_trampoline_buffer_builder_t, - f: *const wasmer_trampoline_callable_t, + builder: *mut wasmer_trampoline_buffer_builder_t, + func: *const wasmer_trampoline_callable_t, ctx: *const c_void, ) -> usize { - let b = &mut *(b as *mut TrampolineBufferBuilder); - b.add_context_trampoline(f as *const CallTarget, ctx as *const CallContext) + let builder = &mut *(builder as *mut TrampolineBufferBuilder); + builder.add_context_trampoline(func as *const CallTarget, ctx as *const CallContext) } +/// Adds a callinfo trampoline to the builder. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_add_callinfo_trampoline( - b: *mut wasmer_trampoline_buffer_builder_t, - f: *const wasmer_trampoline_callable_t, + builder: *mut wasmer_trampoline_buffer_builder_t, + func: *const wasmer_trampoline_callable_t, ctx: *const c_void, num_params: u32, ) -> usize { - let b = &mut *(b as *mut TrampolineBufferBuilder); - b.add_callinfo_trampoline( - ::std::mem::transmute(f), - ctx as *const CallContext, - num_params, - ) + let builder = &mut *(builder as *mut TrampolineBufferBuilder); + builder.add_callinfo_trampoline(mem::transmute(func), ctx as *const CallContext, num_params) } +/// Finalizes the trampoline builder into an executable buffer. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_builder_build( - b: *mut wasmer_trampoline_buffer_builder_t, + builder: *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 _ + let builder = Box::from_raw(builder as *mut TrampolineBufferBuilder); + Box::into_raw(Box::new(builder.build())) as *mut _ } +/// Destroys the trampoline buffer if not null. #[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); +pub unsafe extern "C" fn wasmer_trampoline_buffer_destroy(buffer: *mut wasmer_trampoline_buffer_t) { + if !buffer.is_null() { + Box::from_raw(buffer); + } } +/// Returns the callable pointer for the trampoline with index `idx`. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_buffer_get_trampoline( - b: *const wasmer_trampoline_buffer_t, + buffer: *const wasmer_trampoline_buffer_t, idx: usize, ) -> *const wasmer_trampoline_callable_t { - let b = &*(b as *const TrampolineBuffer); - b.get_trampoline(idx) as _ + let buffer = &*(buffer as *const TrampolineBuffer); + buffer.get_trampoline(idx) as _ } +/// Returns the context added by `add_context_trampoline`, from within the callee function. #[no_mangle] #[allow(clippy::cast_ptr_alignment)] pub unsafe extern "C" fn wasmer_trampoline_get_context() -> *mut c_void { diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index e03b6fb58..7c0f5249a 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -596,24 +596,45 @@ 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_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *b, - const wasmer_trampoline_callable_t *f, +/** + * Adds a callinfo trampoline to the builder. + */ +uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder, + const wasmer_trampoline_callable_t *func, const void *ctx, uint32_t num_params); -uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *b, - const wasmer_trampoline_callable_t *f, +/** + * Adds a context trampoline to the builder. + */ +uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *builder, + const wasmer_trampoline_callable_t *func, const void *ctx); -wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *b); +/** + * Finalizes the trampoline builder into an executable buffer. + */ +wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *builder); +/** + * Creates a new trampoline builder. + */ wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(void); -void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *b); +/** + * Destroys the trampoline buffer if not null. + */ +void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer); -const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *b, +/** + * Returns the callable pointer for the trampoline with index `idx`. + */ +const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *buffer, uintptr_t idx); +/** + * Returns the context added by `add_context_trampoline`, from within the callee function. + */ void *wasmer_trampoline_get_context(void); /** diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 7e3c6612f..1c00b74c3 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -470,24 +470,31 @@ 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_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *b, - const wasmer_trampoline_callable_t *f, +/// Adds a callinfo trampoline to the builder. +uintptr_t wasmer_trampoline_buffer_builder_add_callinfo_trampoline(wasmer_trampoline_buffer_builder_t *builder, + const wasmer_trampoline_callable_t *func, const void *ctx, uint32_t num_params); -uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *b, - const wasmer_trampoline_callable_t *f, +/// Adds a context trampoline to the builder. +uintptr_t wasmer_trampoline_buffer_builder_add_context_trampoline(wasmer_trampoline_buffer_builder_t *builder, + const wasmer_trampoline_callable_t *func, const void *ctx); -wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *b); +/// Finalizes the trampoline builder into an executable buffer. +wasmer_trampoline_buffer_t *wasmer_trampoline_buffer_builder_build(wasmer_trampoline_buffer_builder_t *builder); +/// Creates a new trampoline builder. wasmer_trampoline_buffer_builder_t *wasmer_trampoline_buffer_builder_new(); -void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *b); +/// Destroys the trampoline buffer if not null. +void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer); -const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *b, +/// Returns the callable pointer for the trampoline with index `idx`. +const wasmer_trampoline_callable_t *wasmer_trampoline_buffer_get_trampoline(const wasmer_trampoline_buffer_t *buffer, uintptr_t idx); +/// Returns the context added by `add_context_trampoline`, from within the callee function. void *wasmer_trampoline_get_context(); /// Returns true for valid wasm bytes and false for invalid bytes From 66dcec91c4b7014565ab5aed0aeaa6869424d735 Mon Sep 17 00:00:00 2001 From: losfair Date: Wed, 5 Jun 2019 01:59:20 +0800 Subject: [PATCH 11/11] Add comments for `trampoline_x64`. --- lib/runtime-core/src/trampoline_x64.rs | 47 ++++++++++++++++++++------ 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/runtime-core/src/trampoline_x64.rs b/lib/runtime-core/src/trampoline_x64.rs index 83dbf355e..4defd1fa7 100644 --- a/lib/runtime-core/src/trampoline_x64.rs +++ b/lib/runtime-core/src/trampoline_x64.rs @@ -7,8 +7,12 @@ //! Variadic functions are not supported because `rax` is used by the trampoline code. use crate::loader::CodeMemory; +use std::{mem, slice}; lazy_static! { + /// Reads the context pointer from `mm0`. + /// + /// This function generates code at runtime since `asm!` macro is not yet stable. static ref GET_CONTEXT: extern "C" fn () -> *const CallContext = { static CODE: &'static [u8] = &[ 0x48, 0x0f, 0x7e, 0xc0, // movq %mm0, %rax @@ -18,33 +22,39 @@ lazy_static! { mem[..CODE.len()].copy_from_slice(CODE); mem.make_executable(); let ptr = mem.as_ptr(); - ::std::mem::forget(mem); + mem::forget(mem); unsafe { - ::std::mem::transmute(ptr) + mem::transmute(ptr) } }; } +/// An opaque type for pointers to a callable memory location. pub enum CallTarget {} + +/// An opaque type for context pointers. pub enum CallContext {} + +/// An opaque type for generated trampolines' call entries. pub enum Trampoline {} +/// Trampoline Buffer Builder. pub struct TrampolineBufferBuilder { code: Vec, offsets: Vec, } +/// Trampoline Buffer. pub struct TrampolineBuffer { code: CodeMemory, offsets: Vec, } fn value_to_bytes(ptr: &T) -> &[u8] { - unsafe { - ::std::slice::from_raw_parts(ptr as *const T as *const u8, ::std::mem::size_of::()) - } + unsafe { slice::from_raw_parts(ptr as *const T as *const u8, mem::size_of::()) } } +/// Calls `GET_CONTEXT` and returns the current context. pub fn get_context() -> *const CallContext { GET_CONTEXT() } @@ -57,6 +67,13 @@ impl TrampolineBufferBuilder { } } + /// Adds a context trampoline. + /// + /// This generates a transparent trampoline function that forwards any call to `target` with + /// unmodified params/returns. When called from the trampoline, `target` will have access to + /// the `context` specified here through `get_context()`. + /// + /// Note that since `rax` is overwritten internally, variadic functions are not supported as `target`. pub fn add_context_trampoline( &mut self, target: *const CallTarget, @@ -81,6 +98,13 @@ impl TrampolineBufferBuilder { idx } + /// Adds a callinfo trampoline. + /// + /// This generates a trampoline function that collects `num_params` parameters into an array + /// and passes the array into `target` as the second argument when called. The first argument + /// of `target` is the `context` specified here. + /// + /// Note that non-integer parameters/variadic functions are not supported. pub fn add_callinfo_trampoline( &mut self, target: unsafe extern "C" fn(*const CallContext, *const u64) -> u64, @@ -151,6 +175,7 @@ impl TrampolineBufferBuilder { idx } + /// Consumes the builder and builds the trampoline buffer. pub fn build(self) -> TrampolineBuffer { get_context(); // ensure lazy initialization is completed @@ -165,6 +190,7 @@ impl TrampolineBufferBuilder { } impl TrampolineBuffer { + /// Returns the trampoline pointer at index `idx`. pub fn get_trampoline(&self, idx: usize) -> *const Trampoline { &self.code[self.offsets[idx]] as *const u8 as *const Trampoline } @@ -190,8 +216,7 @@ mod tests { ); 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 { mem::transmute::<_, extern "C" fn(i32, f32) -> f32>(t)(1, 2.0) as i32 }; assert_eq!(ret, 6); } #[test] @@ -201,7 +226,7 @@ mod tests { } unsafe extern "C" fn do_add(ctx: *const CallContext, args: *const u64) -> u64 { let ctx = &*(ctx as *const TestContext); - let args: &[u64] = ::std::slice::from_raw_parts(args, 8); + let args: &[u64] = slice::from_raw_parts(args, 8); (args.iter().map(|x| *x as i32).fold(0, |a, b| a + b) + ctx.value) as u64 } let mut builder = TrampolineBufferBuilder::new(); @@ -211,9 +236,9 @@ mod tests { let buf = builder.build(); let t = buf.get_trampoline(idx); let ret = unsafe { - ::std::mem::transmute::<_, extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32) -> i32>( - t, - )(1, 2, 3, 4, 5, 6, 7, 8) as i32 + mem::transmute::<_, extern "C" fn(i32, i32, i32, i32, i32, i32, i32, i32) -> i32>(t)( + 1, 2, 3, 4, 5, 6, 7, 8, + ) as i32 }; assert_eq!(ret, 136); }