mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 06:15:33 +00:00
Merge branch 'master' into command/pyodide
This commit is contained in:
commit
a9bcb7d932
@ -95,6 +95,8 @@ pub mod instance;
|
|||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod module;
|
pub mod module;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||||
|
pub mod trampoline;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
|
84
lib/runtime-c-api/src/trampoline.rs
Normal file
84
lib/runtime-c-api/src/trampoline.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//! Trampoline emitter for transforming function calls.
|
||||||
|
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::mem;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
{
|
||||||
|
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(
|
||||||
|
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||||
|
func: *const wasmer_trampoline_callable_t,
|
||||||
|
ctx: *const c_void,
|
||||||
|
) -> usize {
|
||||||
|
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(
|
||||||
|
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||||
|
func: *const wasmer_trampoline_callable_t,
|
||||||
|
ctx: *const c_void,
|
||||||
|
num_params: u32,
|
||||||
|
) -> usize {
|
||||||
|
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(
|
||||||
|
builder: *mut wasmer_trampoline_buffer_builder_t,
|
||||||
|
) -> *mut wasmer_trampoline_buffer_t {
|
||||||
|
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(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(
|
||||||
|
buffer: *const wasmer_trampoline_buffer_t,
|
||||||
|
idx: usize,
|
||||||
|
) -> *const wasmer_trampoline_callable_t {
|
||||||
|
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 {
|
||||||
|
get_context() as *const c_void as *mut c_void
|
||||||
|
}
|
132
lib/runtime-c-api/tests/test-import-function-callinfo.c
Normal file
132
lib/runtime-c-api/tests/test-import-function-callinfo.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "../wasmer.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -14,8 +14,15 @@ typedef struct {
|
|||||||
int value;
|
int value;
|
||||||
} context_data;
|
} context_data;
|
||||||
|
|
||||||
|
struct print_str_context {
|
||||||
|
int call_count;
|
||||||
|
};
|
||||||
|
|
||||||
void print_str(wasmer_instance_context_t *ctx, int32_t ptr, int32_t len)
|
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);
|
const wasmer_memory_t *memory = wasmer_instance_context_memory(ctx, 0);
|
||||||
uint32_t mem_len = wasmer_memory_length(memory);
|
uint32_t mem_len = wasmer_memory_length(memory);
|
||||||
uint8_t *mem_bytes = wasmer_memory_data(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 params_sig[] = {WASM_I32, WASM_I32};
|
||||||
wasmer_value_tag returns_sig[] = {};
|
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_context_trampoline(
|
||||||
|
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");
|
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;
|
wasmer_import_t import;
|
||||||
|
|
||||||
char *module_name = "env";
|
char *module_name = "env";
|
||||||
@ -95,7 +115,10 @@ int main()
|
|||||||
assert(ptr_len == 13);
|
assert(ptr_len == 13);
|
||||||
assert(0 == strcmp(actual_str, "Hello, World!"));
|
assert(0 == strcmp(actual_str, "Hello, World!"));
|
||||||
assert(context_data_value == actual_context_data_value);
|
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");
|
printf("Destroying func\n");
|
||||||
wasmer_import_func_destroy(func);
|
wasmer_import_func_destroy(func);
|
||||||
printf("Destroy instance\n");
|
printf("Destroy instance\n");
|
||||||
|
@ -133,6 +133,18 @@ typedef struct {
|
|||||||
|
|
||||||
} wasmer_serialized_module_t;
|
} 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.
|
* Creates a new Module from the given wasm bytes.
|
||||||
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
* Returns `wasmer_result_t::WASMER_OK` upon success.
|
||||||
@ -584,6 +596,47 @@ uint32_t wasmer_table_length(wasmer_table_t *table);
|
|||||||
*/
|
*/
|
||||||
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
|
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the trampoline buffer if not null.
|
||||||
|
*/
|
||||||
|
void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true for valid wasm bytes and false for invalid bytes
|
* Returns true for valid wasm bytes and false for invalid bytes
|
||||||
*/
|
*/
|
||||||
|
@ -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" {
|
extern "C" {
|
||||||
|
|
||||||
/// Creates a new Module from the given wasm bytes.
|
/// Creates a new Module from the given wasm bytes.
|
||||||
@ -458,6 +470,33 @@ uint32_t wasmer_table_length(wasmer_table_t *table);
|
|||||||
/// and `wasmer_last_error_message` to get an error message.
|
/// and `wasmer_last_error_message` to get an error message.
|
||||||
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
|
wasmer_result_t wasmer_table_new(wasmer_table_t **table, wasmer_limits_t limits);
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// 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();
|
||||||
|
|
||||||
|
/// Destroys the trampoline buffer if not null.
|
||||||
|
void wasmer_trampoline_buffer_destroy(wasmer_trampoline_buffer_t *buffer);
|
||||||
|
|
||||||
|
/// 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
|
/// Returns true for valid wasm bytes and false for invalid bytes
|
||||||
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
bool wasmer_validate(const uint8_t *wasm_bytes, uint32_t wasm_bytes_len);
|
||||||
|
|
||||||
|
@ -8,6 +8,10 @@ extern crate field_offset;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -29,12 +33,16 @@ mod sig_registry;
|
|||||||
pub mod structures;
|
pub mod structures;
|
||||||
mod sys;
|
mod sys;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||||
|
pub mod trampoline_x64;
|
||||||
pub mod typed_func;
|
pub mod typed_func;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod units;
|
pub mod units;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod vmcalls;
|
pub mod vmcalls;
|
||||||
|
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||||
|
pub use trampoline_x64 as trampoline;
|
||||||
|
|
||||||
use self::error::CompileResult;
|
use self::error::CompileResult;
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
|
245
lib/runtime-core/src/trampoline_x64.rs
Normal file
245
lib/runtime-core/src/trampoline_x64.rs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
//! 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;
|
||||||
|
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
|
||||||
|
0xc3, // retq
|
||||||
|
];
|
||||||
|
let mut mem = CodeMemory::new(4096);
|
||||||
|
mem[..CODE.len()].copy_from_slice(CODE);
|
||||||
|
mem.make_executable();
|
||||||
|
let ptr = mem.as_ptr();
|
||||||
|
mem::forget(mem);
|
||||||
|
unsafe {
|
||||||
|
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<u8>,
|
||||||
|
offsets: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trampoline Buffer.
|
||||||
|
pub struct TrampolineBuffer {
|
||||||
|
code: CodeMemory,
|
||||||
|
offsets: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_to_bytes<T: Copy>(ptr: &T) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(ptr as *const T as *const u8, mem::size_of::<T>()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls `GET_CONTEXT` and returns the current context.
|
||||||
|
pub fn get_context() -> *const CallContext {
|
||||||
|
GET_CONTEXT()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrampolineBufferBuilder {
|
||||||
|
pub fn new() -> TrampolineBufferBuilder {
|
||||||
|
TrampolineBufferBuilder {
|
||||||
|
code: vec![],
|
||||||
|
offsets: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
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(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(value_to_bytes(&target));
|
||||||
|
self.code.extend_from_slice(&[
|
||||||
|
0xff, 0xe0, // jmpq *%rax
|
||||||
|
]);
|
||||||
|
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,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the builder and builds the trampoline buffer.
|
||||||
|
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 {
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn test_context_trampoline() {
|
||||||
|
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_context_trampoline(
|
||||||
|
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 { 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] = 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 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user