feat: libsledge pattern for wasmception (#299)
* feat: libsledge pattern for wasmception * ci: build libsledge * ci: fix YAML whitespace issue * ci: fix another YAML whitespace issue * fix: Correct type issues in memcpy * ci: debug permissions * ci: restore build rules * chore: Change wasmception init symbol * chore: Assored cleanup * fix: update g0 cache on preemption * refactor: inline global vec in sandbox struct * chore: trailing newline on JSON * chore: Clean up extraneous file associations * docs: Explain export and weak symbols * docs: Remove comment * refactor: Replace CHAR_BIT with limits.h * refactor: Remove redundant unlikely define * docs: Wasmception fn type checking disabled notewasmception-bench wasmception
parent
bbaae33ce9
commit
5d19891e63
@ -1 +1 @@
|
||||
Subproject commit 782efc4448889ed4e89fe57ed548e1907f36eafa
|
||||
Subproject commit 90b535a67ca09421e55b74de06f4a8a817131a8c
|
@ -0,0 +1,45 @@
|
||||
CFILES := src/*.c
|
||||
INCLUDES := -Iinclude/
|
||||
|
||||
CFLAGS := -fPIC -O3
|
||||
|
||||
# CFI Sanitizer
|
||||
# CFLAGS+=-fvisibility=default -fsanitize=cfi
|
||||
|
||||
# Undefined Sanitizer - https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
|
||||
# CFLAGS+=-fsanitize=undefined,float-divide-by-zero,unsigned-integer-overflow,implicit-conversion,local-bounds,nullability
|
||||
|
||||
# Address Sanitizer - "Fast Memory Error Detector" - https://clang.llvm.org/docs/AddressSanitizer.html
|
||||
# CFLAGS+=-fsanitize=address -fno-omit-frame-pointer
|
||||
|
||||
# Clang SafeStack - https://clang.llvm.org/docs/SafeStack.html
|
||||
# CFLAGS+=-fsanitize=safe-stack
|
||||
|
||||
# Memory Sanitizer - https://clang.llvm.org/docs/MemorySanitizer.html
|
||||
# CFLAGS+=-fsanitize=memory -fno-omit-frame-pointer
|
||||
|
||||
all: dist/libsledge.a
|
||||
|
||||
dist:
|
||||
mkdir dist
|
||||
|
||||
dist/memory_instructions.o: src/memory_instructions.c dist
|
||||
clang ${CFLAGS} -c ${INCLUDES} -o $@ $<
|
||||
|
||||
dist/numeric_instructions.o: src/numeric_instructions.c dist
|
||||
clang ${CFLAGS} -c ${INCLUDES} -o $@ $<
|
||||
|
||||
dist/table_instructions.o: src/table_instructions.c dist
|
||||
clang ${CFLAGS} -c ${INCLUDES} -o $@ $<
|
||||
|
||||
dist/variable_instructions.o: src/variable_instructions.c dist
|
||||
clang ${CFLAGS} -c ${INCLUDES} -o $@ $<
|
||||
|
||||
dist/instantiation.o: src/instantiation.c dist
|
||||
clang ${CFLAGS} -c ${INCLUDES} -o $@ $<
|
||||
|
||||
dist/libsledge.a: dist/memory_instructions.o dist/numeric_instructions.o dist/table_instructions.o dist/variable_instructions.o dist/instantiation.o
|
||||
ar rcs dist/libsledge.a $^
|
||||
|
||||
clean:
|
||||
rm -rf dist
|
@ -0,0 +1,164 @@
|
||||
---
|
||||
geometry: margin=2cm
|
||||
---
|
||||
|
||||
# libsledge Binary Interfaces
|
||||
|
||||
libsledge is a \*.a static library (archive) that is statically linked with a \*.bc file generated by the aWsm compiler when compiling to a \*.so Linux shared library that can be loaded and executed by the sledgert runtime.
|
||||
|
||||
The static library internally implements the aWsm ABI in order to link to the \*.bc file generated by the aWsm compiler. [See the relevant documentation for this ABI here](../awsm/doc/abi.md).
|
||||
|
||||
libsledge defines a ABI between the sledgert runtime and a \*.so shared library containing an executable serverless function. This is distinct from the aWsm ABI.
|
||||
|
||||
# SLEdge \*.so serverless module
|
||||
|
||||
A SLEdge \*.so serverless module is generated by the latter portion of the aWsm/SLEdge toolchain.
|
||||
|
||||
The first portion of the toolchain is responsible for compiling a source program into a WebAssembly module. This is handled by standard compilers capable of emitting WebAssembly.
|
||||
The second portion of the toolchain is the aWsm compiler, which generates a \*.bc file with a well defined ABI
|
||||
The third portion of the toolchain is the LLVM compiler, which ingests a \*.bc file emitted by aWsm and the libsledge static library, and emits a SLEdge \*.so serverless module.
|
||||
|
||||
## Architecture
|
||||
|
||||
In order to reduce the overhead of calling sledgert functions, libsledge operates on global state of type `sledge_abi__wasm_module_instance` at `sledge_abi__current_wasm_module_instance`. This represents the global state of the wasm32 context executing on a sledgert worker core. The scheduler is responsible for populating these symbols before yielding execution to a serverless function.
|
||||
|
||||
The `sledge_abi__wasm_module_instance` structure includes the WebAssembly function table and the WebAssembly linear memory. This subset was selected because the author believes that use of function pointers and linear memory is frequent enough that LTO when compiling the \*.so file is beneficial.
|
||||
|
||||
All WebAssembly state
|
||||
|
||||
## WebAssembly Instruction Implementation
|
||||
|
||||
Here is a list of WebAssembly instructions that depend on symbols from libsledge, libc, or sledgert (via the SLEdge ABI).
|
||||
|
||||
### [Control Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#control-instructions)
|
||||
|
||||
The ABI includes the
|
||||
|
||||
| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI |
|
||||
| ------------- | ------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| call_indirect | `get_function_from_table` | `stderr`, `fprintf` | `sledge_abi__current_wasm_module_instance.table`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_INVALID_INDEX`, `WASM_TRAP_MISMATCHED_FUNCTION_TYPE` |
|
||||
|
||||
### [Variable Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#variable-instructions)
|
||||
|
||||
| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI |
|
||||
| ----------- | ---------------------------------- | ----------------- | ---------------------------------------------------------------------- |
|
||||
| global.get | `get_global_i32`, `get_global_i64` | None | `sledge_abi__wasm_globals_get_i32`, `sledge_abi__wasm_globals_get_i64` |
|
||||
| global.set | `set_global_i32`, `set_global_i64` | None | `sledge_abi__wasm_globals_set_i32`, `sledge_abi__wasm_globals_set_i64` |
|
||||
|
||||
### [Numeric Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#numeric-instructions)
|
||||
|
||||
| Instruction | aWsm ABI | libc Dependencies | SLEdge ABI |
|
||||
| --------------- | ---------------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| i32.div_s | `i32_div` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.div_u | `u32_div` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.rem_s | `i32_rem` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.rem_u | `u32_rem` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.rotl | `rotl_u32` | None | None |
|
||||
| i32.rotr | `rotr_u32` | None | None |
|
||||
| i32.trunc_f32_s | `i32_trunc_f32` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.trunc_f32_u | `u32_trunc_f32` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.trunc_f64_s | `i32_trunc_f64` ("fast unsafe" disabled) | `INT32_MIN`, `INT32_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i32.trunc_f64_u | `u32_trunc_f64` ("fast unsafe" disabled) | `UINT32_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.div_s | `i64_div` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.div_u | `u64_div` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.rem_s | `i64_rem` ("fast unsafe" disabled) | `INT32_MIN`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.rem_u | `u64_rem` ("fast unsafe" disabled) | `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.rotl | `rotl_u64` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| i64.rotr | `rotr_u64` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| i64.trunc_f32_s | `i64_trunc_f32` | `INT64_MIN`, `INT64_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.trunc_f32_u | `u64_trunc_f32` | `UINT64_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.trunc_f64_s | `i64_trunc_f64` | `INT64_MIN`, `INT64_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| i64.trunc_f64_u | `u64_trunc_f64` | `UINT64_MAX`, `stderr`, `fprintf` | `sledge_abi__wasm_trap_raise`, `WASM_TRAP_ILLEGAL_ARITHMETIC_OPERATION` |
|
||||
| f32.ceil | `f32_ceil` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| f32.copysign | `f32_copysign` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| f32.floor | `f32_floor` | `floor` | None |
|
||||
| f32.max | `f32_max` | None | None |
|
||||
| f32.min | `f32_min` | None | None |
|
||||
| f32.nearest | `f32_nearest` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| f32.trunc | `f32_trunc_f32` | `trunc` | None |
|
||||
| f64.ceil | `f64_ceil` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| f64.floor | `f64_floor` | `floor` | None |
|
||||
| f64.max | `f64_max` | None | None |
|
||||
| f64.min | `f64_min` | None | None |
|
||||
| f64.nearest | `f64_nearest` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
| f64.trunc | `f64_trunc_f64` | **NOT SUPPORTED** | **NOT SUPPORTED** |
|
||||
|
||||
### [Memory Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions)
|
||||
|
||||
| instruction | aWsm ABI | libc Dependencies | SLEdge ABI |
|
||||
| ------------ | ------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| i32.load | `get_i32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.load8_s | `get_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.load8_u | `get_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.load16_s | `get_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.load16_u | `get_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.store | `set_i32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.store8 | `set_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i32.store16 | `set_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load | `get_i64` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load8_s | `get_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load8_u | `get_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load16_s | `get_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load16_u | `get_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load32_s | `get_i32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.load32_u | `get_i32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.store | `set_i64` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.store8 | `set_i8` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.store16 | `set_i16` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| i64.store32 | `set_i32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| f32.load | `get_f32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| f32.store | `set_f32` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| f64.load | `get_f64` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| f64.store | `set_f64` | `fprintf`, `stderr` | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_trap_raise`, `WASM_TRAP_OUT_OF_BOUNDS_LINEAR_MEMORY` |
|
||||
| memory.grow | `instruction_memory_grow` | | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_memory_expand` |
|
||||
| memory.size | `instruction_memory_size` | | `sledge_abi__current_wasm_module_instance.memory` |
|
||||
| None | `initialize_region` | | `sledge_abi__current_wasm_module_instance.memory`, `sledge_abi__wasm_memory_initialize_region` |
|
||||
|
||||
Discussion:
|
||||
|
||||
- Should `instruction_memory_grow` be moved into sledgert? This would simplify the handling of the "cache" and generating a memory profile?
|
||||
- Rename `sledge_abi__wasm_globals_*` to `sledge_abi__wasm_global_*`
|
||||
- Implement Unsupported Numeric Instructions
|
||||
- Should the wasm global table be accessed directly instead of via a runtime function?
|
||||
- Should the Function Table be handled by the \*.so file or sledgert? Are function pointers really called that frequently?
|
||||
|
||||
# SLEdge \*.so Module Loading / Initialization
|
||||
|
||||
The `sledgert` runtime is invoked with an argument containing the path to a JSON file defining serverless functions. The JSON format is a top level array containing 0..N JSON objects with the following keys:
|
||||
"name" - A Human friendly name identifying a serverless function. Is required.
|
||||
"path" - A path to a \*.so module containing the program to be executed
|
||||
"port" - The port which the serverless function is registered
|
||||
"relative-deadline-us"
|
||||
"expected-execution-us"
|
||||
"admissions-percentile"
|
||||
"http-req-size"
|
||||
"http-resp-size"
|
||||
"http-resp-content-type"
|
||||
|
||||
The path to the JSON file is passed to `module_alloc_from_json`, which uses the Jasmine library to parse the JSON, performs validation, and passes the resulting specification to `module_alloc` for each module definition found. `module_alloc` allocated heap memory for a `struct module` and then calls `module_init`. `module_init` calls `sledge_abi_symbols_init`, which calls `dlopen` on the _.so file at the path specified in the JSON and then calls `dlsym` to resolve symbols within the _.so module.
|
||||
|
||||
`module.abi.initialize_globals` -> `SLEDGE_ABI__INITIALIZE_GLOBALS` -> `populate_globals`
|
||||
`module.abi.initialize_memory`-> `SLEDGE_ABI__INITIALIZE_MEMORY` -> `populate_memory`
|
||||
`module.abi.initialize_table` -> `SLEDGE_ABI__INITIALIZE_TABLE` -> `populate_table`
|
||||
`module.abi.entrypoint` -> `SLEDGE_ABI__ENTRYPOINT` -> `wasmf__start`
|
||||
`module.abi.starting_pages` -> `SLEDGE_ABI__STARTING_PAGES` -> `starting_pages`
|
||||
`module.abi.max_pages` -> `SLEDGE_ABI__MAX_PAGES` -> `max_pages`
|
||||
|
||||
`module init` then calls `module.abi.initialize_table`, which populates the indirect function table with the actual functions. This is performed once during module initialization because this table does not actually vary between instances of a module.
|
||||
|
||||
|
||||
# SLEdge \*.so Module Instantiation
|
||||
|
||||
When `sledgert` receives a request at the registered port specified in the JSON, it performs assorted allocation and initialization steps. The scheduler sets the expected ABI symbols and yields to `current_sandbox_start`, which immediately calls `current_sandbox_init`. This function initializes the associated runtime state and
|
||||
|
||||
1. calls `module.abi.initialize_globals` for the current sandbox if not NULL. This is optional because the module might not have been built with the `--runtime-globals`, in which case runtime globals are not used at all. If not NULL, the globals are set in the table.
|
||||
2. calls `module.abi.initialize_memory`, which copies regions into the linear memory
|
||||
|
||||
`current_sandbox_init` calls `wasi_context_init` to initialize the WASI context within the runtime.
|
||||
|
||||
`current_sandbox_init` returns to `current_sandbox_start`, which sets up wasm traps using `setjmp` and then calls `module.abi.entrypoint`
|
||||
|
||||
# Questions:
|
||||
|
||||
- Should `sledge_abi__current_wasm_module_instance` be turned into a macro defined int the ABI header?
|
||||
- What happens if the runtime is executing and calls `sledge_abi__current_wasm_module_instance_trap`?
|
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <threads.h>
|
||||
|
||||
/* Do not include runtime headers here! */
|
||||
|
||||
/** ABI Types
|
||||
* Changes to these types breaks the contract between sledgert and the *.so modules that it runs. This means that all
|
||||
* modules must be recompiled. Avoid this!
|
||||
*/
|
||||
|
||||
struct sledge_abi__wasm_table_entry {
|
||||
uint32_t type_id;
|
||||
void * func_pointer;
|
||||
};
|
||||
|
||||
struct sledge_abi__wasm_table {
|
||||
uint32_t length;
|
||||
uint32_t capacity;
|
||||
struct sledge_abi__wasm_table_entry *buffer; /* Backing heap allocation */
|
||||
};
|
||||
|
||||
struct sledge_abi__wasm_memory {
|
||||
uint64_t size; /* Initial Size in bytes */
|
||||
uint64_t capacity; /* Size backed by actual pages */
|
||||
uint64_t max; /* Soft cap in bytes. Defaults to 4GB */
|
||||
uint8_t *buffer; /* Backing heap allocation. Different lifetime because realloc might move this */
|
||||
};
|
||||
|
||||
/* This structure is the runtime representation of the unique state of a module instance
|
||||
* Currently this is not spec-compliant, as it only supports a single table and a single memory and it excludes many
|
||||
* entities https://webassembly.github.io/spec/core/exec/runtime.html#module-instances
|
||||
*/
|
||||
struct sledge_abi__wasm_module_instance {
|
||||
uint64_t wasmg_0; /* Used to refer to the shadow stack */
|
||||
struct sledge_abi__wasm_table *table;
|
||||
/* Note: memory has an opaque type due to private state. Do not reorder members below this! */
|
||||
struct sledge_abi__wasm_memory memory;
|
||||
/* Private runtime state follows */
|
||||
};
|
||||
|
||||
/* Symbols expected from sledgert */
|
||||
|
||||
extern int32_t sledge_abi__wasm_memory_expand(struct sledge_abi__wasm_memory *wasm_memory, uint32_t page_count);
|
||||
void sledge_abi__wasm_memory_initialize_region(struct sledge_abi__wasm_memory *wasm_memory, uint32_t offset,
|
||||
uint32_t region_size, uint8_t region[]);
|
||||
|
||||
extern int32_t sledge_abi__wasm_globals_get_i32(uint32_t idx);
|
||||
extern int64_t sledge_abi__wasm_globals_get_i64(uint32_t idx);
|
||||
extern int32_t sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable);
|
||||
extern int32_t sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable);
|
||||
|
||||
/* Wasm initialization functions generated by the aWsm compiler */
|
||||
extern void sledge_abi__init_globals(void);
|
||||
extern void sledge_abi__init_mem(void);
|
||||
extern void sledge_abi__init_tbl(void);
|
||||
extern int32_t sledge_abi__entrypoint(int32_t, int32_t);
|
||||
extern uint32_t sledge_abi__wasm_memory_starting_pages(void);
|
||||
extern uint32_t sledge_abi__wasm_memory_max_pages(void);
|
||||
|
||||
typedef void (*sledge_abi__init_globals_fn_t)(void);
|
||||
#define SLEDGE_ABI__INITIALIZE_GLOBALS "sledge_abi__init_globals"
|
||||
|
||||
typedef void (*sledge_abi__init_mem_fn_t)(void);
|
||||
#define SLEDGE_ABI__INITIALIZE_MEMORY "sledge_abi__init_mem"
|
||||
|
||||
typedef void (*sledge_abi__init_tbl_fn_t)(void);
|
||||
#define SLEDGE_ABI__INITIALIZE_TABLE "sledge_abi__init_tbl"
|
||||
|
||||
typedef void (*sledge_abi__init_libc_fn_t)(int32_t, int32_t);
|
||||
#define SLEDGE_ABI__INITIALIZE_LIBC "sledge_abi__init_libc"
|
||||
|
||||
typedef int32_t (*sledge_abi__entrypoint_fn_t)(int32_t a, int32_t b);
|
||||
#define SLEDGE_ABI__ENTRYPOINT "sledge_abi__entrypoint"
|
||||
|
||||
typedef uint32_t (*sledge_abi__wasm_memory_starting_pages_fn_t)(void);
|
||||
#define SLEDGE_ABI__STARTING_PAGES "sledge_abi__wasm_memory_starting_pages"
|
||||
|
||||
typedef uint32_t (*sledge_abi__wasm_memory_max_pages_fn_t)(void);
|
||||
#define SLEDGE_ABI__MAX_PAGES "sledge_abi__wasm_memory_max_pages"
|
@ -0,0 +1,68 @@
|
||||
#include "sledge_abi.h"
|
||||
|
||||
/* The visibility attribute is used to control the visibility of a symbol across C translation units. The default
|
||||
* argument forces "external" linkage. This originated in gcc, but had been adopted by LLVM.
|
||||
* Reference: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
|
||||
/* The weak attribute is used to provide a weak symbol that can be overridden by later linking to a "strong" symbol.
|
||||
* This is useful for defining a default or no-op implementation of a symbol that might or might not be present in a
|
||||
* binary. This applies to populate_globals() below, which is only generated by aWsm when a module is compiled with the
|
||||
* "--runtime-globals" argument. We need to provide a weak symbol that does nothing to prevent unresolved symbols from
|
||||
* triggering linker errors. */
|
||||
#define WEAK __attribute__((weak))
|
||||
|
||||
/* aWsm ABI Symbols */
|
||||
extern void populate_globals(void);
|
||||
extern void populate_memory(void);
|
||||
extern void populate_table(void);
|
||||
extern void populate_table(void);
|
||||
extern void wasmf___init_libc(int32_t, int32_t);
|
||||
extern int32_t wasmf_main(int32_t, int32_t);
|
||||
extern uint32_t starting_pages;
|
||||
extern uint32_t max_pages;
|
||||
|
||||
WEAK void populate_globals(){};
|
||||
|
||||
EXPORT void
|
||||
sledge_abi__init_globals(void)
|
||||
{
|
||||
populate_globals();
|
||||
}
|
||||
|
||||
void
|
||||
sledge_abi__init_mem(void)
|
||||
{
|
||||
populate_memory();
|
||||
}
|
||||
|
||||
EXPORT void
|
||||
sledge_abi__init_tbl(void)
|
||||
{
|
||||
populate_table();
|
||||
}
|
||||
|
||||
// Wasmception Initialization. Unsure what a and b is here
|
||||
EXPORT void
|
||||
sledge_abi__init_libc(int32_t envp, int32_t pn)
|
||||
{
|
||||
wasmf___init_libc(envp, pn);
|
||||
}
|
||||
|
||||
EXPORT int32_t
|
||||
sledge_abi__entrypoint(int32_t argc, int32_t argv)
|
||||
{
|
||||
return wasmf_main(argc, argv);
|
||||
}
|
||||
|
||||
EXPORT uint32_t
|
||||
sledge_abi__wasm_memory_starting_pages(void)
|
||||
{
|
||||
return starting_pages;
|
||||
}
|
||||
|
||||
EXPORT uint32_t
|
||||
sledge_abi__wasm_memory_max_pages(void)
|
||||
{
|
||||
return max_pages;
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
#include "sledge_abi.h"
|
||||
|
||||
#define INLINE __attribute__((always_inline))
|
||||
#define WASM_PAGE_SIZE (1024 * 64) /* 64KB */
|
||||
#define likely(X) __builtin_expect(!!(X), 1)
|
||||
#define unlikely(X) __builtin_expect(!!(X), 0)
|
||||
|
||||
/* This is private and NOT in the sledge_abi.h header because the runtime uses an overlay struct that extends this
|
||||
* symbol with private members */
|
||||
extern thread_local struct sledge_abi__wasm_module_instance sledge_abi__current_wasm_module_instance;
|
||||
|
||||
INLINE uint32_t
|
||||
instruction_memory_size()
|
||||
{
|
||||
return (uint32_t)(sledge_abi__current_wasm_module_instance.memory.size / WASM_PAGE_SIZE);
|
||||
}
|
||||
|
||||
// These functions are equivalent to those in wasm_memory.h, but they minimize pointer dereferencing
|
||||
INLINE float
|
||||
get_f32(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(float) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(float *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE double
|
||||
get_f64(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(double) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(double *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int8_t
|
||||
get_i8(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int8_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(int8_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int16_t
|
||||
get_i16(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int16_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(int16_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int32_t
|
||||
get_i32(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int32_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(int32_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int64_t
|
||||
get_i64(uint32_t offset)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int64_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
return *(int64_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
// Now setting routines
|
||||
INLINE void
|
||||
set_f32(uint32_t offset, float value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(float) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(float *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_f64(uint32_t offset, double value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(double) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(double *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i8(uint32_t offset, int8_t value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int8_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(int8_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i16(uint32_t offset, int16_t value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
|
||||
assert(offset + sizeof(int16_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(int16_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i32(uint32_t offset, int32_t value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int32_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(int32_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i64(uint32_t offset, int64_t value)
|
||||
{
|
||||
assert(sledge_abi__current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int64_t) <= sledge_abi__current_wasm_module_instance.memory.size);
|
||||
*(int64_t *)&sledge_abi__current_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stub that implements the WebAssembly memory.grow instruction
|
||||
*
|
||||
* @param count number of pages to grow the WebAssembly linear memory by
|
||||
* @return The previous size of the linear memory in pages or -1 if enough memory cannot be allocated
|
||||
*/
|
||||
INLINE int32_t
|
||||
instruction_memory_grow(uint32_t count)
|
||||
{
|
||||
return sledge_abi__wasm_memory_expand(&sledge_abi__current_wasm_module_instance.memory, count);
|
||||
}
|
||||
|
||||
|
||||
INLINE void
|
||||
initialize_region(uint32_t offset, uint32_t region_size, uint8_t region[region_size])
|
||||
{
|
||||
sledge_abi__wasm_memory_initialize_region(&sledge_abi__current_wasm_module_instance.memory, offset, region_size,
|
||||
region);
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "sledge_abi.h"
|
||||
|
||||
#define CHAR_BIT 8
|
||||
|
||||
// TODO: Throughout here we use `assert` for error conditions, which isn't optimal
|
||||
// Instead we should use `unlikely` branches to a single trapping function (which should optimize better)
|
||||
// The below functions are for implementing WASM instructions
|
||||
#define INLINE __attribute__((always_inline))
|
||||
#define likely(X) __builtin_expect(!!(X), 1)
|
||||
#define unlikely(X) __builtin_expect(!!(X), 0)
|
||||
|
||||
// ROTL and ROTR helper functions
|
||||
INLINE uint32_t
|
@ -0,0 +1,50 @@
|
||||
#include "sledge_abi.h"
|
||||
|
||||
#define INDIRECT_TABLE_SIZE (1 << 10)
|
||||
#define INLINE __attribute__((always_inline))
|
||||
#define likely(X) __builtin_expect(!!(X), 1)
|
||||
#define unlikely(X) __builtin_expect(!!(X), 0)
|
||||
|
||||
/* This is private and NOT in the sledge_abi.h header because the runtime uses an overlay struct that extends this
|
||||
* symbol with private members */
|
||||
extern thread_local struct sledge_abi__wasm_module_instance sledge_abi__current_wasm_module_instance;
|
||||
|
||||
static INLINE void *
|
||||
wasm_table_get(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t type_id)
|
||||
{
|
||||
assert(wasm_table != NULL);
|
||||
assert(idx < wasm_table->capacity);
|
||||
|
||||
struct sledge_abi__wasm_table_entry f = wasm_table->buffer[idx];
|
||||
|
||||
/* Wasmception-based modules trigger function type mismatches for an unknown reason.
|
||||
* This should be reenabled when WASI is added */
|
||||
// assert(f.type_id != type_id);
|
||||
assert(f.func_pointer != NULL);
|
||||
|
||||
return f.func_pointer;
|
||||
}
|
||||
|
||||
static INLINE void
|
||||
wasm_table_set(struct sledge_abi__wasm_table *wasm_table, uint32_t idx, uint32_t type_id, char *pointer)
|
||||
{
|
||||
assert(wasm_table != NULL);
|
||||
assert(idx < wasm_table->capacity);
|
||||
assert(pointer != NULL);
|
||||
|
||||
if (wasm_table->buffer[idx].type_id == type_id && wasm_table->buffer[idx].func_pointer == pointer) return;
|
||||
wasm_table->buffer[idx] = (struct sledge_abi__wasm_table_entry){ .type_id = type_id, .func_pointer = pointer };
|
||||
}
|
||||
|
||||
INLINE void
|
||||
add_function_to_table(uint32_t idx, uint32_t type_id, char *pointer)
|
||||
{
|
||||
wasm_table_set(sledge_abi__current_wasm_module_instance.table, idx, type_id, pointer);
|
||||
}
|
||||
|
||||
/* char * is used as a generic pointer to a function pointer */
|
||||
INLINE char *
|
||||
get_function_from_table(uint32_t idx, uint32_t type_id)
|
||||
{
|
||||
return wasm_table_get(sledge_abi__current_wasm_module_instance.table, idx, type_id);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sledge_abi.h"
|
||||
|
||||
/* This is private and NOT in the sledge_abi.h header because the runtime uses an overlay struct that extends this
|
||||
* symbol with private members */
|
||||
extern thread_local struct sledge_abi__wasm_module_instance sledge_abi__current_wasm_module_instance;
|
||||
|
||||
int32_t
|
||||
get_global_i32(uint32_t idx)
|
||||
{
|
||||
if (idx == 0) {
|
||||
return (int32_t)sledge_abi__current_wasm_module_instance.wasmg_0;
|
||||
} else {
|
||||
return sledge_abi__wasm_globals_get_i32(idx);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t
|
||||
get_global_i64(uint32_t idx)
|
||||
{
|
||||
if (idx == 0) {
|
||||
return (int64_t)sledge_abi__current_wasm_module_instance.wasmg_0;
|
||||
} else {
|
||||
return sledge_abi__wasm_globals_get_i64(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_global_i32(uint32_t idx, int32_t value)
|
||||
{
|
||||
if (idx == 0) sledge_abi__current_wasm_module_instance.wasmg_0 = (uint64_t)value;
|
||||
|
||||
/* aWsm does not currently pass the is_mutable flag, so all runtime globals are assumed to be mutable.
|
||||
This is true if aWsm uses the flags to inline constant globals */
|
||||
int rc = sledge_abi__wasm_globals_set_i32(idx, value, true);
|
||||
assert(rc == 0);
|
||||
}
|
||||
|
||||
void
|
||||
set_global_i64(uint32_t idx, int64_t value)
|
||||
{
|
||||
if (idx == 0) sledge_abi__current_wasm_module_instance.wasmg_0 = (uint64_t)value;
|
||||
|
||||
/* aWsm does not currently pass the is_mutable flag, so all runtime globals are assumed to be mutable.
|
||||
This is true if aWsm uses the flags to inline constant globals */
|
||||
int rc = sledge_abi__wasm_globals_set_i64(idx, value, true);
|
||||
assert(rc == 0);
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "current_wasm_module_instance.h"
|
||||
|
||||
INLINE uint32_t
|
||||
instruction_memory_size()
|
||||
{
|
||||
return (uint32_t)(current_wasm_module_instance.memory.size / WASM_PAGE_SIZE);
|
||||
}
|
||||
|
||||
// These functions are equivalent to those in wasm_memory.h, but they minimize pointer dereferencing
|
||||
INLINE float
|
||||
get_f32(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(float) <= current_wasm_module_instance.memory.size);
|
||||
return *(float *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE double
|
||||
get_f64(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(double) <= current_wasm_module_instance.memory.size);
|
||||
return *(double *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int8_t
|
||||
get_i8(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int8_t) <= current_wasm_module_instance.memory.size);
|
||||
return *(int8_t *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int16_t
|
||||
get_i16(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int16_t) <= current_wasm_module_instance.memory.size);
|
||||
return *(int16_t *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int32_t
|
||||
get_i32(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int32_t) <= current_wasm_module_instance.memory.size);
|
||||
return *(int32_t *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int64_t
|
||||
get_i64(uint32_t offset)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int64_t) <= current_wasm_module_instance.memory.size);
|
||||
return *(int64_t *)¤t_wasm_module_instance.memory.buffer[offset];
|
||||
}
|
||||
|
||||
INLINE int32_t
|
||||
get_global_i32(uint32_t offset)
|
||||
{
|
||||
return get_i32(offset);
|
||||
}
|
||||
|
||||
INLINE int64_t
|
||||
get_global_i64(uint32_t offset)
|
||||
{
|
||||
return get_i64(offset);
|
||||
}
|
||||
|
||||
// Now setting routines
|
||||
INLINE void
|
||||
set_f32(uint32_t offset, float value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(float) <= current_wasm_module_instance.memory.size);
|
||||
*(float *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_f64(uint32_t offset, double value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(double) <= current_wasm_module_instance.memory.size);
|
||||
*(double *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i8(uint32_t offset, int8_t value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int8_t) <= current_wasm_module_instance.memory.size);
|
||||
*(int8_t *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i16(uint32_t offset, int16_t value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
|
||||
assert(offset + sizeof(int16_t) <= current_wasm_module_instance.memory.size);
|
||||
*(int16_t *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i32(uint32_t offset, int32_t value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int32_t) <= current_wasm_module_instance.memory.size);
|
||||
*(int32_t *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_i64(uint32_t offset, int64_t value)
|
||||
{
|
||||
assert(current_wasm_module_instance.memory.buffer != NULL);
|
||||
assert(offset + sizeof(int64_t) <= current_wasm_module_instance.memory.size);
|
||||
*(int64_t *)¤t_wasm_module_instance.memory.buffer[offset] = value;
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_global_i32(uint32_t offset, int32_t value)
|
||||
{
|
||||
set_i32(offset, value);
|
||||
}
|
||||
|
||||
INLINE void
|
||||
set_global_i64(uint32_t offset, int64_t value)
|
||||
{
|
||||
set_i64(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Stub that implements the WebAssembly memory.grow instruction
|
||||
*
|
||||
* @param count number of pages to grow the WebAssembly linear memory by
|
||||
* @return The previous size of the linear memory in pages or -1 if enough memory cannot be allocated
|
||||
*/
|
||||
INLINE int32_t
|
||||
instruction_memory_grow(uint32_t count)
|
||||
{
|
||||
int old_page_count = current_wasm_module_instance.memory.size / WASM_PAGE_SIZE;
|
||||
|
||||
/* Return -1 if we've hit the linear memory max */
|
||||
int rc = wasm_memory_expand(¤t_wasm_module_instance.memory, WASM_PAGE_SIZE * count);
|
||||
if (unlikely(rc == -1)) return -1;
|
||||
|
||||
/* We updated "forked state" in current_wasm_module_instance.memory. We need to write this back to persist */
|
||||
current_wasm_module_instance_memory_writeback();
|
||||
|
||||
#ifdef LOG_SANDBOX_MEMORY_PROFILE
|
||||
// Cache the runtime of the first N page allocations
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (likely(sandbox->timestamp_of.page_allocations_size < SANDBOX_PAGE_ALLOCATION_TIMESTAMP_COUNT)) {
|
||||
sandbox->timestamp_of.page_allocations[sandbox->timestamp_of.page_allocations_size++] =
|
||||
sandbox->duration_of_state.running
|
||||
+ (uint32_t)(__getcycles() - sandbox->timestamp_of.last_state_change);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
INLINE void
|
||||
initialize_region(uint32_t offset, uint32_t region_size, uint8_t region[region_size])
|
||||
{
|
||||
wasm_memory_initialize_region(¤t_wasm_module_instance.memory, offset, region_size, region);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
#include "types.h"
|
||||
#include "wasm_module_instance.h"
|
||||
|
||||
extern thread_local struct wasm_module_instance current_wasm_module_instance;
|
||||
|
||||
INLINE void
|
||||
add_function_to_table(uint32_t idx, uint32_t type_id, char *pointer)
|
||||
{
|
||||
wasm_table_set(current_wasm_module_instance.table, idx, type_id, pointer);
|
||||
}
|
||||
|
||||
/* char * is used as a generic pointer to a function pointer */
|
||||
INLINE char *
|
||||
get_function_from_table(uint32_t idx, uint32_t type_id)
|
||||
{
|
||||
return wasm_table_get(current_wasm_module_instance.table, idx, type_id);
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "debuglog.h"
|
||||
#include "wasm_types.h"
|
||||
|
||||
struct awsm_abi {
|
||||
void * handle;
|
||||
awsm_abi_init_globals_fn_t initialize_globals;
|
||||
awsm_abi_init_mem_fn_t initialize_memory;
|
||||
awsm_abi_init_tbl_fn_t initialize_tables;
|
||||
awsm_abi_init_libc_fn_t initialize_libc;
|
||||
awsm_abi_entrypoint_fn_t entrypoint;
|
||||
uint32_t starting_pages;
|
||||
uint32_t max_pages;
|
||||
};
|
||||
|
||||
/* Initializes the ABI object using the *.so file at path */
|
||||
static inline int
|
||||
awsm_abi_init(struct awsm_abi *abi, char *path)
|
||||
{
|
||||
assert(abi != NULL);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
abi->handle = dlopen(path, RTLD_LAZY | RTLD_DEEPBIND);
|
||||
if (abi->handle == NULL) {
|
||||
fprintf(stderr, "Failed to open %s with error: %s\n", path, dlerror());
|
||||
goto dl_open_error;
|
||||
};
|
||||
|
||||
/* Resolve the symbols in the dynamic library *.so file */
|
||||
abi->entrypoint = (awsm_abi_entrypoint_fn_t)dlsym(abi->handle, AWSM_ABI_ENTRYPOINT);
|
||||
if (abi->entrypoint == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_ENTRYPOINT, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This symbol may or may not be present depending on whether the aWsm was
|
||||
* run with the --runtime-globals flag. It is not clear what the proper
|
||||
* configuration would be for SLEdge, so no validation is performed
|
||||
*/
|
||||
abi->initialize_globals = (awsm_abi_init_globals_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_GLOBALS);
|
||||
|
||||
abi->initialize_memory = (awsm_abi_init_mem_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_MEMORY);
|
||||
if (abi->initialize_memory == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_MEMORY, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
};
|
||||
|
||||
abi->initialize_tables = (awsm_abi_init_tbl_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_TABLE);
|
||||
if (abi->initialize_tables == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_TABLE, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
};
|
||||
|
||||
abi->initialize_libc = (awsm_abi_init_libc_fn_t)dlsym(abi->handle, AWSM_ABI_INITIALIZE_LIBC);
|
||||
if (abi->initialize_libc == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_INITIALIZE_LIBC, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
|
||||
abi->starting_pages = *(uint32_t *)dlsym(abi->handle, AWSM_ABI_STARTING_PAGES);
|
||||
if (abi->starting_pages == 0) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_STARTING_PAGES, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
|
||||
abi->max_pages = *(uint32_t *)dlsym(abi->handle, AWSM_ABI_MAX_PAGES);
|
||||
if (abi->max_pages == 0) {
|
||||
/* This seems to not always be present. I assume this is only there if the source module explicitly
|
||||
* specified this */
|
||||
abi->max_pages = WASM_MEMORY_PAGES_MAX;
|
||||
debuglog("max_pages symbols not defined. Defaulting to MAX defined by spec.\n");
|
||||
|
||||
// TODO: We need to prove that this actually can get generated by awsm
|
||||
// fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", AWSM_ABI_MAX_PAGES, path,
|
||||
// dlerror());
|
||||
// goto dl_error;
|
||||
}
|
||||
|
||||
|
||||
done:
|
||||
return rc;
|
||||
dl_error:
|
||||
dlclose(abi->handle);
|
||||
dl_open_error:
|
||||
rc = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
static inline int
|
||||
awsm_abi_deinit(struct awsm_abi *abi)
|
||||
{
|
||||
abi->entrypoint = NULL;
|
||||
abi->initialize_globals = NULL;
|
||||
abi->initialize_memory = NULL;
|
||||
abi->initialize_tables = NULL;
|
||||
abi->initialize_libc = NULL;
|
||||
|
||||
int rc = dlclose(abi->handle);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to close *.so file with error: %s\n", dlerror());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "debuglog.h"
|
||||
#include "wasm_types.h"
|
||||
#include "sledge_abi.h"
|
||||
|
||||
struct sledge_abi_symbols {
|
||||
void * handle;
|
||||
sledge_abi__init_globals_fn_t initialize_globals;
|
||||
sledge_abi__init_mem_fn_t initialize_memory;
|
||||
sledge_abi__init_tbl_fn_t initialize_tables;
|
||||
sledge_abi__init_libc_fn_t initialize_libc;
|
||||
sledge_abi__entrypoint_fn_t entrypoint;
|
||||
uint32_t starting_pages;
|
||||
uint32_t max_pages;
|
||||
};
|
||||
|
||||
/* Initializes the ABI object using the *.so file at path */
|
||||
static inline int
|
||||
sledge_abi_symbols_init(struct sledge_abi_symbols *abi, char *path)
|
||||
{
|
||||
assert(abi != NULL);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
abi->handle = dlopen(path, RTLD_LAZY | RTLD_DEEPBIND);
|
||||
if (abi->handle == NULL) {
|
||||
fprintf(stderr, "Failed to open %s with error: %s\n", path, dlerror());
|
||||
goto dl_open_error;
|
||||
};
|
||||
|
||||
/* Resolve the symbols in the dynamic library *.so file */
|
||||
abi->entrypoint = (sledge_abi__entrypoint_fn_t)dlsym(abi->handle, SLEDGE_ABI__ENTRYPOINT);
|
||||
if (abi->entrypoint == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__ENTRYPOINT, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This symbol may or may not be present depending on whether the aWsm was
|
||||
* run with the --runtime-globals flag. It is not clear what the proper
|
||||
* configuration would be for SLEdge, so no validation is performed
|
||||
*/
|
||||
abi->initialize_globals = (sledge_abi__init_globals_fn_t)dlsym(abi->handle, SLEDGE_ABI__INITIALIZE_GLOBALS);
|
||||
|
||||
abi->initialize_memory = (sledge_abi__init_mem_fn_t)dlsym(abi->handle, SLEDGE_ABI__INITIALIZE_MEMORY);
|
||||
if (abi->initialize_memory == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__INITIALIZE_MEMORY,
|
||||
path, dlerror());
|
||||
goto dl_error;
|
||||
};
|
||||
|
||||
abi->initialize_tables = (sledge_abi__init_tbl_fn_t)dlsym(abi->handle, SLEDGE_ABI__INITIALIZE_TABLE);
|
||||
if (abi->initialize_tables == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__INITIALIZE_TABLE,
|
||||
path, dlerror());
|
||||
goto dl_error;
|
||||
};
|
||||
|
||||
abi->initialize_libc = (sledge_abi__init_libc_fn_t)dlsym(abi->handle, SLEDGE_ABI__INITIALIZE_LIBC);
|
||||
if (abi->initialize_libc == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__INITIALIZE_LIBC, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
};
|
||||
|
||||
sledge_abi__wasm_memory_starting_pages_fn_t get_starting_pages = dlsym(abi->handle, SLEDGE_ABI__STARTING_PAGES);
|
||||
if (get_starting_pages == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__STARTING_PAGES, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
|
||||
abi->starting_pages = get_starting_pages();
|
||||
|
||||
sledge_abi__wasm_memory_max_pages_fn_t get_max_pages = dlsym(abi->handle, SLEDGE_ABI__MAX_PAGES);
|
||||
if (get_max_pages == NULL) {
|
||||
fprintf(stderr, "Failed to resolve symbol %s in %s with error: %s\n", SLEDGE_ABI__MAX_PAGES, path,
|
||||
dlerror());
|
||||
goto dl_error;
|
||||
}
|
||||
abi->max_pages = get_max_pages();
|
||||
|
||||
done:
|
||||
return rc;
|
||||
dl_error:
|
||||
dlclose(abi->handle);
|
||||
dl_open_error:
|
||||
rc = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
static inline int
|
||||
sledge_abi_symbols_deinit(struct sledge_abi_symbols *abi)
|
||||
{
|
||||
abi->entrypoint = NULL;
|
||||
abi->initialize_globals = NULL;
|
||||
abi->initialize_memory = NULL;
|
||||
abi->initialize_tables = NULL;
|
||||
abi->initialize_libc = NULL;
|
||||
|
||||
int rc = dlclose(abi->handle);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to close *.so file with error: %s\n", dlerror());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <vec.h>
|
||||
|
||||
/* https://webassembly.github.io/spec/core/syntax/modules.html#globals */
|
||||
|
||||
/* This only supports i32 and i64 because this is all that aWsm currently supports */
|
||||
enum wasm_global_type
|
||||
{
|
||||
WASM_GLOBAL_TYPE_UNUSED,
|
||||
WASM_GLOBAL_TYPE_I32,
|
||||
WASM_GLOBAL_TYPE_I64,
|
||||
// WASM_GLOBAL_TYPE_F32,
|
||||
// WASM_GLOBAL_TYPE_F64,
|
||||
// WASM_GLOBAL_TYPE_V128,
|
||||
// WASM_GLOBAL_TYPE_FUNCREF,
|
||||
// WASM_GLOBAL_TYPE_EXTERNREF,
|
||||
};
|
||||
|
||||
|
||||
union wasm_global_value {
|
||||
int32_t i32;
|
||||
int64_t i64;
|
||||
// float f32;
|
||||
// double f64;
|
||||
};
|
||||
|
||||
typedef struct wasm_global {
|
||||
enum wasm_global_type type;
|
||||
bool mut;
|
||||
union wasm_global_value value;
|
||||
} wasm_global_t;
|
||||
|
||||
VEC(wasm_global_t)
|
||||
|
||||
static inline void
|
||||
wasm_globals_deinit(struct vec_wasm_global_t *globals)
|
||||
{
|
||||
vec_wasm_global_t_deinit(globals);
|
||||
}
|
||||
|
||||
static inline int
|
||||
wasm_globals_init(struct vec_wasm_global_t *globals, uint32_t capacity)
|
||||
{
|
||||
return vec_wasm_global_t_init(globals, capacity);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wasm_globals_update_if_used(struct vec_wasm_global_t *globals, uint32_t idx, uint64_t *dest)
|
||||
{
|
||||
wasm_global_t *global = vec_wasm_global_t_get(globals, idx);
|
||||
if (likely(global->type != WASM_GLOBAL_TYPE_UNUSED)) *dest = (uint64_t)global->value.i64;
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
wasm_globals_get_i32(struct vec_wasm_global_t *globals, uint32_t idx)
|
||||
{
|
||||
wasm_global_t *global = vec_wasm_global_t_get(globals, idx);
|
||||
|
||||
assert(global != NULL);
|
||||
assert(global->type == WASM_GLOBAL_TYPE_I32);
|
||||
|
||||
return global->value.i32;
|
||||
}
|
||||
|
||||
static inline int64_t
|
||||
wasm_globals_get_i64(struct vec_wasm_global_t *globals, uint32_t idx)
|
||||
{
|
||||
wasm_global_t *global = vec_wasm_global_t_get(globals, idx);
|
||||
|
||||
assert(global != NULL);
|
||||
assert(global->type == WASM_GLOBAL_TYPE_I64);
|
||||
|
||||
return global->value.i64;
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
wasm_globals_set_i32(struct vec_wasm_global_t *globals, uint32_t idx, int32_t value, bool is_mutable)
|
||||
{
|
||||
wasm_global_t *current = vec_wasm_global_t_get(globals, idx);
|
||||
assert(current->type == WASM_GLOBAL_TYPE_UNUSED || current->mut == true);
|
||||
|
||||
int rc = vec_wasm_global_t_insert(globals, idx,
|
||||
(wasm_global_t){
|
||||
.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I32, .value = value });
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline int32_t
|
||||
wasm_globals_set_i64(struct vec_wasm_global_t *globals, uint32_t idx, int64_t value, bool is_mutable)
|
||||
{
|
||||
wasm_global_t *current = vec_wasm_global_t_get(globals, idx);
|
||||
assert(current->type == WASM_GLOBAL_TYPE_UNUSED || current->mut == true);
|
||||
|
||||
int rc = vec_wasm_global_t_insert(globals, idx,
|
||||
(wasm_global_t){
|
||||
.mut = is_mutable, .type = WASM_GLOBAL_TYPE_I64, .value = value });
|
||||
return rc;
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
#include "wasm_memory.h"
|
||||
#include "wasm_table.h"
|
||||
#include "sledge_abi.h"
|
||||
|
||||
/* This structure is the runtime representation of the unique state of a module instance
|
||||
* Currently this is not spec-compliant, as it only supports a single table and a single memory and it excludes many
|
||||
* entities https://webassembly.github.io/spec/core/exec/runtime.html#module-instances
|
||||
*/
|
||||
struct wasm_module_instance {
|
||||
struct wasm_memory memory;
|
||||
struct wasm_table *table;
|
||||
/* Public */
|
||||
struct sledge_abi__wasm_module_instance abi;
|
||||
};
|
||||
|
@ -0,0 +1,64 @@
|
||||
#include "current_sandbox.h"
|
||||
#include "sledge_abi.h"
|
||||
#include "wasm_memory.h"
|
||||
|
||||
EXPORT int32_t
|
||||
sledge_abi__wasm_memory_expand(struct sledge_abi__wasm_memory *wasm_memory, uint32_t page_count)
|
||||
{
|
||||
int32_t old_page_count = wasm_memory->size / WASM_PAGE_SIZE;
|
||||
|
||||
int rc = wasm_memory_expand((struct wasm_memory *)wasm_memory, page_count * WASM_PAGE_SIZE);
|
||||
if (unlikely(rc == -1)) return -1;
|
||||
|
||||
/* We updated "forked state" in sledge_abi__current_wasm_module_instance.memory. We need to write this back to
|
||||
* the original struct as well */
|
||||
current_sandbox_memory_writeback();
|
||||
|
||||
#ifdef LOG_SANDBOX_MEMORY_PROFILE
|
||||
// Cache the runtime of the first N page allocations
|
||||
for (int i = 0; i < page_count; i++) {
|
||||
if (likely(sandbox->timestamp_of.page_allocations_size < SANDBOX_PAGE_ALLOCATION_TIMESTAMP_COUNT)) {
|
||||
sandbox->timestamp_of.page_allocations[sandbox->timestamp_of.page_allocations_size++] =
|
||||
sandbox->duration_of_state.running
|
||||
+ (uint32_t)(__getcycles() - sandbox->timestamp_of.last_state_change);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return old_page_count;
|
||||
}
|
||||
|
||||
EXPORT void
|
||||
sledge_abi__wasm_memory_initialize_region(struct sledge_abi__wasm_memory *wasm_memory, uint32_t offset,
|
||||
uint32_t region_size, uint8_t region[])
|
||||
{
|
||||
return wasm_memory_initialize_region((struct wasm_memory *)wasm_memory, offset, region_size, region);
|
||||
}
|
||||
|
||||
EXPORT int32_t
|
||||
sledge_abi__wasm_globals_get_i32(uint32_t idx)
|
||||
{
|
||||
struct sandbox *current = current_sandbox_get();
|
||||
return wasm_globals_get_i32(¤t->globals, idx);
|
||||
}
|
||||
|
||||
EXPORT int64_t
|
||||
sledge_abi__wasm_globals_get_i64(uint32_t idx)
|
||||
{
|
||||
struct sandbox *current = current_sandbox_get();
|
||||
return wasm_globals_get_i64(¤t->globals, idx);
|
||||
}
|
||||
|
||||
EXPORT int32_t
|
||||
sledge_abi__wasm_globals_set_i32(uint32_t idx, int32_t value, bool is_mutable)
|
||||
{
|
||||
struct sandbox *current = current_sandbox_get();
|
||||
return wasm_globals_set_i32(¤t->globals, idx, value, true);
|
||||
}
|
||||
|
||||
EXPORT int32_t
|
||||
sledge_abi__wasm_globals_set_i64(uint32_t idx, int64_t value, bool is_mutable)
|
||||
{
|
||||
struct sandbox *current = current_sandbox_get();
|
||||
return wasm_globals_set_i64(¤t->globals, idx, value, true);
|
||||
}
|
Loading…
Reference in new issue