-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
278 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
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] | ||
#[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, | ||
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] | ||
#[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 { | ||
let b = Box::from_raw(b as *mut TrampolineBufferBuilder); | ||
Box::into_raw(Box::new(b.build())) as *mut _ | ||
} | ||
|
||
#[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, | ||
) -> *const wasmer_trampoline_callable_t { | ||
let b = &*(b as *const TrampolineBuffer); | ||
b.get_trampoline(idx) as _ | ||
} | ||
|
||
#[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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
//! 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<u8>, | ||
offsets: Vec<usize>, | ||
} | ||
|
||
pub struct TrampolineBuffer { | ||
code: CodeMemory, | ||
offsets: Vec<usize>, | ||
} | ||
|
||
fn pointer_to_bytes<T>(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); | ||
} | ||
} |