Implement libc-WASI for Linux SGX platform and update documents (#343)
This commit is contained in:
@ -6,8 +6,6 @@ Embedding WAMR guideline
|
||||
|
||||
## The runtime initialization
|
||||
|
||||
|
||||
|
||||
``` C
|
||||
char *buffer, error_buf[128];
|
||||
wasm_module_t module;
|
||||
@ -16,64 +14,61 @@ Embedding WAMR guideline
|
||||
wasm_exec_env_t exec_env;
|
||||
uint32 size, stack_size = 8092, heap_size = 8092;
|
||||
|
||||
// initialize the wasm runtime by default configurations
|
||||
/* initialize the wasm runtime by default configurations */
|
||||
wasm_runtime_init();
|
||||
|
||||
// read WASM file into a memory buffer
|
||||
/* read WASM file into a memory buffer */
|
||||
buffer = read_wasm_binary_to_buffer(…, &size);
|
||||
|
||||
// Add it below if runtime needs to export native functions to WASM APP
|
||||
// wasm_runtime_register_natives(...)
|
||||
/* add line below if we want to export native functions to WASM app */
|
||||
wasm_runtime_register_natives(...);
|
||||
|
||||
// parse the WASM file from buffer and create a WASM module
|
||||
/* parse the WASM file from buffer and create a WASM module */
|
||||
module = wasm_runtime_load(buffer, size, error_buf, sizeof(error_buf));
|
||||
|
||||
// create an instance of the WASM module (WASM linear memory is ready)
|
||||
module_inst = wasm_runtime_instantiate(module,
|
||||
stack_size,
|
||||
heap_size,
|
||||
error_buf,
|
||||
sizeof(error_buf));
|
||||
/* create an instance of the WASM module (WASM linear memory is ready) */
|
||||
module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
|
||||
error_buf, sizeof(error_buf));
|
||||
```
|
||||
|
||||
The `wasm_runtime_init()` will use the default memory allocator from the [`core/shared/platform`](../core/shared/platform) for the runtime memory management.
|
||||
The `wasm_runtime_init()` uses the default memory allocator os_malloc/os_free function from the [`core/shared/platform`](../core/shared/platform) for the runtime memory management.
|
||||
|
||||
|
||||
|
||||
The WAMR supports to restrict its all memory allocations in a raw buffer. It ensures the dynamics by the WASM applications won't harm the system availability, which is extremely important for embedded systems. This can be done by using `wasm_runtime_full_init()`. This function also allows you to configure the native APIs for exporting to WASM app.
|
||||
WAMR supports to restrict its all memory allocations in a raw buffer. It ensures the dynamic memories used by the WASM applications won't harm the system availability, which is extremely important for embedded systems. This can be done by using `wasm_runtime_full_init()`. This function also allows you to configure the native API's for exporting to WASM app, and set the maximum thread number when multi-thread feature is enabled.
|
||||
|
||||
Refer to the following sample:
|
||||
|
||||
```c
|
||||
// the native functions that will be exported to WASM app
|
||||
/* the native functions that will be exported to WASM app */
|
||||
static NativeSymbol native_symbols[] = {
|
||||
EXPORT_WASM_API_WITH_SIG(display_input_read, "(*)i"),
|
||||
EXPORT_WASM_API_WITH_SIG(display_flush, "(iiii*)")
|
||||
};
|
||||
|
||||
// all the runtime memory allocations are retricted in the global_heap_buf array
|
||||
/* all the runtime memory allocations are retricted in the global_heap_buf array */
|
||||
static char global_heap_buf[512 * 1024];
|
||||
RuntimeInitArgs init_args;
|
||||
memset(&init_args, 0, sizeof(RuntimeInitArgs));
|
||||
|
||||
// configure the memory allocator for the runtime
|
||||
/* configure the memory allocator for the runtime */
|
||||
init_args.mem_alloc_type = Alloc_With_Pool;
|
||||
init_args.mem_alloc_option.pool.heap_buf = global_heap_buf;
|
||||
init_args.mem_alloc_option.pool.heap_size = sizeof(global_heap_buf);
|
||||
|
||||
// configure the native functions being exported to WASM app
|
||||
/* configure the native functions being exported to WASM app */
|
||||
init_args.native_module_name = "env";
|
||||
init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
|
||||
init_args.native_symbols = native_symbols;
|
||||
|
||||
/* set maximum thread number if needed when multi-thread is enabled,
|
||||
the default value is 4 */
|
||||
init_args.max_thread_num = max_thread_num;
|
||||
|
||||
/* initialize runtime environment with user configurations*/
|
||||
if (!wasm_runtime_full_init(&init_args)) {
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Native calls WASM functions and passes parameters
|
||||
|
||||
After a module is instantiated, the runtime native can lookup WASM functions by the names and call them.
|
||||
@ -81,28 +76,27 @@ After a module is instantiated, the runtime native can lookup WASM functions by
|
||||
```c
|
||||
unit32 argv[2];
|
||||
|
||||
// lookup a WASM function by its name.
|
||||
// The function signature can NULL here
|
||||
/* lookup a WASM function by its name
|
||||
The function signature can NULL here */
|
||||
func = wasm_runtime_lookup_function(module_inst, "fib", NULL);
|
||||
|
||||
// creat a excution environment which can be used by executing WASM functions
|
||||
/* creat an execution environment to execute the WASM functions */
|
||||
exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
|
||||
|
||||
// arguments are always transferred in 32 bits element
|
||||
/* arguments are always transferred in 32-bit element */
|
||||
argv[0] = 8;
|
||||
|
||||
// call the WASM function
|
||||
/* call the WASM function */
|
||||
if (wasm_runtime_call_wasm(exec_env, func, 1, argv) ) {
|
||||
/* the return value is stored in argv[0] */
|
||||
printf("fib function return: %d\n", argv[0]);
|
||||
/* the return value is stored in argv[0] */
|
||||
printf("fib function return: %d\n", argv[0]);
|
||||
}
|
||||
else {
|
||||
printf("%s\n", wasm_runtime_get_exception(module_inst));
|
||||
/* exception is thrown if call fails */
|
||||
printf("%s\n", wasm_runtime_get_exception(module_inst));
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
The parameters are transferred in an array of 32 bits elements. For parameters that occupy 4 or fewer bytes, each parameter can be a single array element. For parameters in types like double or int64, each parameter will take two array elements. The function return value will be sent back in the first one or two elements of the array according to the value type. See the sample code below:
|
||||
|
||||
```c
|
||||
@ -116,69 +110,60 @@ The parameters are transferred in an array of 32 bits elements. For parameters t
|
||||
argv[0] = arg1;
|
||||
argv[1] = arg2;
|
||||
|
||||
// use memory copy for 8 bytes parameters rather than
|
||||
// *(double*)(&argv[2]) = arg3 here because some archs
|
||||
// like ARM, MIPS requires address is 8 aligned.
|
||||
// Or use the aligned malloc or compiler align attribute
|
||||
// to ensure the array address is 8 bytes aligned
|
||||
/**
|
||||
* use memory copy for 8-byte parameters rather than
|
||||
* *(double*)(&argv[2]) = arg3 here because some archs
|
||||
* like ARM, MIPS require the address must be 8-byte aligned.
|
||||
* Or use the aligned malloc or compiler align attribute
|
||||
* to ensure the array address is 8-byte aligned
|
||||
*/
|
||||
memcpy(&argv[2], &arg3, sizeof(arg3));
|
||||
memcpy(&argv[4], &arg4, sizeof(arg4));
|
||||
//
|
||||
// attention: the arg number is 6 here since both
|
||||
// arg3 and arg4 each takes 2 elements
|
||||
//
|
||||
|
||||
/* attention: the arg number is 6 here since both
|
||||
arg3 and arg4 each takes 2 elements */
|
||||
wasm_runtime_call_wasm(exec_env, func, 6, argv);
|
||||
|
||||
// if the return value is type of 8 bytes, it takes
|
||||
// the first two array elements
|
||||
/* if the return value is type of 8 bytes, it takes
|
||||
the first two array elements */
|
||||
memcpy(&ret, &argv[0], sizeof(ret));
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Pass buffer to WASM function
|
||||
|
||||
|
||||
|
||||
If we need to transfer a buffer to WASM function, we can pass the buffer address through a parameter. **Attention**: The sandbox will forbid the WASM code to access outside memory, we must **allocate the buffer from WASM instance's own memory space and pass the buffer address in instance's space (not the runtime native address)**.
|
||||
|
||||
|
||||
|
||||
There are two runtime APIs available for this purpose.
|
||||
|
||||
```c
|
||||
/*
|
||||
* description: malloc a buffer from instance's private memory space.
|
||||
*
|
||||
* return: the buffer address in instance's memory space (pass to the WASM funciton)
|
||||
* p_native_addr: return the native address of allocated memory
|
||||
* size: the buffer size to allocate
|
||||
*/
|
||||
/**
|
||||
* malloc a buffer from instance's private memory space.
|
||||
*
|
||||
* return: the buffer address in instance's memory space (pass to the WASM funciton)
|
||||
* p_native_addr: return the native address of allocated memory
|
||||
* size: the buffer size to allocate
|
||||
*/
|
||||
int32_t
|
||||
wasm_runtime_module_malloc(wasm_module_inst_t module_inst,
|
||||
uint32_t size,
|
||||
void **p_native_addr);
|
||||
uint32_t size, void **p_native_addr);
|
||||
|
||||
/*
|
||||
* description: malloc a buffer from instance's private memory space,
|
||||
* and copy the data from another native buffer to it.
|
||||
* return: the buffer address in instance's memory space (pass to the WASM funciton)
|
||||
* src: the native buffer address
|
||||
* size: the size of buffer to be allocated and copy data
|
||||
*/
|
||||
/**
|
||||
* malloc a buffer from instance's private memory space,
|
||||
* and copy the data from another native buffer to it.
|
||||
*
|
||||
* return: the buffer address in instance's memory space (pass to the WASM funciton)
|
||||
* src: the native buffer address
|
||||
* size: the size of buffer to be allocated and copy data
|
||||
*/
|
||||
int32
|
||||
wasm_runtime_module_dup_data(WASMModuleInstanceCommon *module_inst,
|
||||
const char *src,
|
||||
uint32 size);
|
||||
const char *src, uint32 size);
|
||||
|
||||
// free the memory allocated from module memory space
|
||||
/* free the memory allocated from module memory space */
|
||||
void
|
||||
wasm_runtime_module_free(wasm_module_inst_t module_inst, int32_t ptr);
|
||||
```
|
||||
|
||||
|
||||
|
||||
Usage sample:
|
||||
|
||||
```c
|
||||
@ -186,29 +171,23 @@ char * buffer = NULL;
|
||||
int32_t buffer_for_wasm;
|
||||
|
||||
buffer_for_wasm = wasm_runtime_module_malloc(module_inst, 100, &buffer);
|
||||
if(buffer_for_wasm != 0)
|
||||
{
|
||||
if (buffer_for_wasm != 0) {
|
||||
unit32 argv[2];
|
||||
strncpy(buffer, "hello", 100); // use native address for accessing in runtime
|
||||
argv[0] = buffer_for_wasm; // pass the buffer address for WASM space.
|
||||
argv[1] = 100; // the size of buffer
|
||||
strncpy(buffer, "hello", 100); /* use native address for accessing in runtime */
|
||||
argv[0] = buffer_for_wasm; /* pass the buffer address for WASM space */
|
||||
argv[1] = 100; /* the size of buffer */
|
||||
wasm_runtime_call_wasm(exec_env, func, 2, argv);
|
||||
|
||||
// it is runtime responsibility to release the memory,
|
||||
// unless the WASM app will free the passed pointer in its code
|
||||
/* it is runtime embedder's responsibility to release the memory,
|
||||
unless the WASM app will free the passed pointer in its code */
|
||||
wasm_runtime_module_free(module_inst, buffer_for_wasm);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Pass structured data to WASM function
|
||||
|
||||
We can't pass structure data or class objects through the pointer since the memory layout can different in two worlds. The way to do it is serialization. Refer to [export_native_api.md](./export_native_api.md) for the details.
|
||||
|
||||
|
||||
|
||||
## Execute wasm functions in multiple threads
|
||||
|
||||
The `exec_env` is not thread safety, it will cause unexpected behavior if the same `exec_env` is used in multiple threads. However, we've provided two ways to execute wasm functions concurrently:
|
||||
@ -219,7 +198,7 @@ The `exec_env` is not thread safety, it will cause unexpected behavior if the sa
|
||||
|
||||
*spawn exec_env:*
|
||||
|
||||
`spawn exec_env` API spawn a `new_exec_env` base on the original `exec_env`, use can use it in other threads:
|
||||
`spawn exec_env` API spawns a `new_exec_env` base on the original `exec_env`, use can use it in other threads:
|
||||
|
||||
```C
|
||||
new_exec_env = wasm_runtime_spawn_exec_env(exec_env);
|
||||
@ -257,7 +236,7 @@ init_args.max_thread_num = THREAD_NUM;
|
||||
/* If this init argument is not set, the default maximum thread number is 4 */
|
||||
```
|
||||
|
||||
**Note2: The wasm application should be built with `--shared-memory` and `-pthread` enabled:**
|
||||
**Note2: The wasm application should be built with `--shared-memory` and `-pthread` enabled:**
|
||||
|
||||
```bash
|
||||
/opt/wasi-sdk/bin/clang -o test.wasm test.c -nostdlib -pthread \
|
||||
@ -274,8 +253,6 @@ init_args.max_thread_num = THREAD_NUM;
|
||||
|
||||
[Here](../samples/spawn-thread) is a sample to show how to use these APIs.
|
||||
|
||||
|
||||
|
||||
## The deinitialization procedure
|
||||
|
||||
```
|
||||
@ -286,8 +263,6 @@ init_args.max_thread_num = THREAD_NUM;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Native calling WASM function working flow
|
||||
|
||||

|
||||
|
||||
Reference in New Issue
Block a user