Skip to content

Commit

Permalink
Try #481:
Browse files Browse the repository at this point in the history
  • Loading branch information
bors[bot] committed Jun 3, 2019
2 parents df486b7 + 06280e2 commit 9722a71
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 1 deletion.
2 changes: 2 additions & 0 deletions lib/runtime-c-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
60 changes: 60 additions & 0 deletions lib/runtime-c-api/src/trampoline.rs
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
}
25 changes: 24 additions & 1 deletion lib/runtime-c-api/tests/test-import-function.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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";
Expand Down Expand Up @@ -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");
Expand Down
27 changes: 27 additions & 0 deletions lib/runtime-c-api/wasmer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
*/
Expand Down
27 changes: 27 additions & 0 deletions lib/runtime-c-api/wasmer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);

Expand Down
8 changes: 8 additions & 0 deletions lib/runtime-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ extern crate field_offset;
#[macro_use]
extern crate serde_derive;

#[allow(unused_imports)]
#[macro_use]
extern crate lazy_static;

#[macro_use]
mod macros;
#[doc(hidden)]
Expand All @@ -29,12 +33,16 @@ 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;
pub mod vm;
#[doc(hidden)]
pub mod vmcalls;
#[cfg(all(unix, target_arch = "x86_64"))]
pub use trampoline_x64 as trampoline;

use self::error::CompileResult;
#[doc(inline)]
Expand Down
130 changes: 130 additions & 0 deletions lib/runtime-core/src/trampoline_x64.rs
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);
}
}

0 comments on commit 9722a71

Please sign in to comment.