From a6578ea92c42e959157121822a77ca46c01f7643 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Mon, 6 Feb 2023 21:03:06 +0800 Subject: [PATCH] init WRT --- .clang-format | 17 +++ .gitignore | 2 + CMakeLists.txt | 46 ++++++++ main.c | 22 ++++ tests/dl.cpp | 128 +++++++++++++++++++++ tests/lib/SimpleLib.cpp | 49 +++++++++ utils/defs.h | 15 +++ utils/log.h | 32 ++++++ wasm.conf | 9 ++ wrt/dl.c | 238 ++++++++++++++++++++++++++++++++++++++++ wrt/dl.h | 27 +++++ wrt/tinymap.c | 130 ++++++++++++++++++++++ wrt/tinymap.h | 32 ++++++ wrt/wrt.c | 167 ++++++++++++++++++++++++++++ wrt/wrt.h | 48 ++++++++ 15 files changed, 962 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 main.c create mode 100644 tests/dl.cpp create mode 100644 tests/lib/SimpleLib.cpp create mode 100644 utils/defs.h create mode 100644 utils/log.h create mode 100644 wasm.conf create mode 100644 wrt/dl.c create mode 100644 wrt/dl.h create mode 100644 wrt/tinymap.c create mode 100644 wrt/tinymap.h create mode 100644 wrt/wrt.c create mode 100644 wrt/wrt.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..185a1d4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +--- +BasedOnStyle: LLVM +AlignConsecutiveMacros: AcrossEmptyLinesAndComments +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: AcrossEmptyLinesAndComments +AlignConsecutiveDeclarations: Consecutive +AlignEscapedNewlines: Left +AllowShortCaseLabelsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +ColumnLimit: 100 +IndentCaseLabels: true +IndentPPDirectives: BeforeHash +IndentWidth: 4 +UseTab: Never +SeparateDefinitionBlocks: Leave +SortIncludes: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8e4c12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.so +cmake-build-* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c1462a4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,46 @@ +cmake_minimum_required(VERSION 3.24) +project(wrt) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmacro-prefix-map=${CMAKE_SOURCE_DIR}=.") +add_definitions(-DWRT_DEBUG=4) + +# math +find_library(M_LIBRARY m) + +# libffi +include(FindPkgConfig) +pkg_check_modules(FFI REQUIRED IMPORTED_TARGET libffi) + +# google test +# tests +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.13.0.zip +) +FetchContent_MakeAvailable(googletest) + +# wamr +include(wasm.conf) +include(${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) +add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) + +# wrt +add_library(wrt wrt/wrt.c wrt/dl.c) +target_include_directories(wrt PRIVATE .) +target_link_libraries(wrt vmlib PkgConfig::FFI ${M_LIBRARY}) + +# main +add_executable(main main.c) +target_include_directories(main PRIVATE .) +target_link_libraries(main wrt) + +# test +enable_testing() +add_executable(test_all tests/dl.cpp) +target_include_directories(test_all PRIVATE .) +target_link_libraries(test_all GTest::gtest_main wrt) +include(GoogleTest) +gtest_discover_tests(test_all) diff --git a/main.c b/main.c new file mode 100644 index 0000000..c23f291 --- /dev/null +++ b/main.c @@ -0,0 +1,22 @@ +#include +#include + +#include "utils/log.h" +#include "wrt/dl.h" +#include "wrt/wrt.h" + +void test_wrt() { + WRTContext context; + wrt_platform_init(&context, malloc, free); + wrt_mem_init(&context, 512 * 1024, 128); + wrt_program_init(&context, NULL, 0, NULL, 0); + wrt_wamr_init(&context, "entry", 8092, 8092); + wrt_run(&context); + wrt_free(&context); +} + +int main() { + printf("Hello, World!\n"); + + return 0; +} \ No newline at end of file diff --git a/tests/dl.cpp b/tests/dl.cpp new file mode 100644 index 0000000..5845dcb --- /dev/null +++ b/tests/dl.cpp @@ -0,0 +1,128 @@ +#include +#include + +extern "C" { +#include "utils/defs.h" +#include "wrt/dl.h" + +extern DLContext dl_context; +extern bool signature_check(const char *signature, int *arg_count, bool *has_ret); +} + +#define check_symbol_consistency(sym, postfix) \ + do { \ + ASSERT_GT((sym), 0); \ + ASSERT_LT((sym), DL_MAX_SYMBOLS); \ + ASSERT_NE(dl_context.sym[(sym)-1].symbol, nullptr); \ + ASSERT_NE(dl_context.sym[(sym)-1].cif, nullptr); \ + ASSERT_GT(dl_context.sym[(sym)-1].backref, 0); \ + unsigned ref##postfix = dl_context.sym[(sym)-1].backref; \ + ASSERT_GT(ref##postfix, 0); \ + ASSERT_LE(ref##postfix, DL_MAX_HANDLES); \ + ASSERT_NE(dl_context.hnd[ref##postfix - 1].handle, nullptr); \ + } while (0) + +#define check_empty(postfix) \ + do { \ + for (auto &hnd##postfix : dl_context.hnd) { \ + ASSERT_EQ(hnd##postfix.handle, nullptr); \ + } \ + for (auto &sym##postfix : dl_context.sym) { \ + ASSERT_EQ(sym##postfix.symbol, nullptr); \ + ASSERT_EQ(sym##postfix.cif, nullptr); \ + ASSERT_EQ(sym##postfix.backref, 0); \ + } \ + } while (0) + +TEST(DLTest, OpenAndClose) { + dl_init(); + + int handle = dl_open(nullptr, "./SimpleLib.so", RTLD_LAZY); + ASSERT_GT(handle, 0); + int result = dl_close(nullptr, handle); + ASSERT_EQ(result, WRT_OK); + check_empty(0); + + handle = dl_open(nullptr, "not_exists", RTLD_LAZY); + ASSERT_EQ(handle, WRT_ERROR); + check_empty(1); + + handle = dl_open(nullptr, "", RTLD_LAZY); + ASSERT_EQ(handle, WRT_ERROR); + check_empty(2); + + handle = dl_open(nullptr, nullptr, RTLD_LAZY); + ASSERT_EQ(handle, WRT_ERROR); + check_empty(3); +} + +TEST(DLTest, ManyOpen) { + dl_init(); + + int handles[DL_MAX_HANDLES]; + for (int &handle : handles) { + handle = dl_open(nullptr, "./SimpleLib.so", RTLD_LAZY); + ASSERT_GT(handle, 0); + } + + { + int handle = dl_open(nullptr, "./SimpleLib.so", RTLD_LAZY); + ASSERT_EQ(handle, WRT_ERROR); + } + + for (int handle : handles) { + int result = dl_close(nullptr, handle); + ASSERT_EQ(result, WRT_OK); + } + + check_empty(0); +} + +TEST(DLTest, SignatureCheck) { + typedef struct { + const char *test; + bool result; + int arg_count; + bool has_ret; + } Case; + + const static Case cases[] = { + {"", false, 0, false}, {"()", true, 0, false}, {"(i)", true, 1, false}, + {"()i", true, 0, true}, {"(x)", false, 0, false}, {"()x", false, 0, false}, + {"(i)f", true, 1, true}, {"(i)x", false, 0, false}, {"(x)f", false, 0, false}, + {"i", false, 0, false}, {"i()", false, 0, false}, {"i(i)", false, 0, false}, + {"(i)()", false, 0, false}, {"(ii", false, 0, false}, {"(iiffdd)l", true, 6, true}, + {"(ffddii)ll", false, 0, false}, {"()ii", false, 0, false}, {"(i", false, 0, false}, + {"(i(i))i", false, 0, false}, {"i)i", false, 0, false}, {"((i)i", false, 0, false}, + {"(i))i", false, 0, false}, {"())", false, 0, false}}; + + for (const auto &cur_case : cases) { + int arg_count = 0; + bool has_ret = false; + bool ok = signature_check(cur_case.test, &arg_count, &has_ret); + + EXPECT_EQ(ok, cur_case.result); + if (ok) { + EXPECT_EQ(arg_count, cur_case.arg_count); + EXPECT_EQ(has_ret, cur_case.has_ret); + } + } +} + +TEST(DLTest, GetSym) { + dl_init(); + + int hnd = dl_open(nullptr, "./SimpleLib.so", RTLD_LAZY); + { + int sym = dl_sym(nullptr, hnd, "fib_fast", "(i)i"); + check_symbol_consistency(sym, 0); + } + + { + int sym = dl_sym(nullptr, hnd, "fib_fast", "(v)i"); + ASSERT_EQ(sym, WRT_ERROR); + } + + dl_close(nullptr, hnd); + check_empty(0); +} diff --git a/tests/lib/SimpleLib.cpp b/tests/lib/SimpleLib.cpp new file mode 100644 index 0000000..f589889 --- /dev/null +++ b/tests/lib/SimpleLib.cpp @@ -0,0 +1,49 @@ +#include + +#define HIDE __attribute__((visibility("hidden"))) + +extern "C" { +int fib_recurse(int n); +int fib_iterate(int n); +int fib_tail_call(int n); +int fib_fast(int n); +} + +int fib_recurse(int n) { + if (n <= 1) { + return n; + } + return fib_recurse(n - 1) + fib_recurse(n - 2); +} + +int fib_iterate(int n) { + int a = 0, b = 1, c; + for (int i = 0; i < n; i++) { + c = a + b; + a = b; + b = c; + } + return a; +} + +HIDE inline int fib_tail(int n, int a, int b) { + if (n == 0) { + return a; + } + return fib_tail(n - 1, b, a + b); +} + +int fib_tail_call(int n) { return fib_tail(n, 0, 1); } + +HIDE inline std::pair fib_fast_internal(int n) { + if (n == 0) return {0, 1}; + auto p = fib_fast_internal(n >> 1); + int c = p.first * (2 * p.second - p.first); + int d = p.first * p.first + p.second * p.second; + if (n & 1) + return {d, c + d}; + else + return {c, d}; +} + +int fib_fast(int n) { return fib_fast_internal(n).first; } diff --git a/utils/defs.h b/utils/defs.h new file mode 100644 index 0000000..fc26150 --- /dev/null +++ b/utils/defs.h @@ -0,0 +1,15 @@ +#ifndef WRT_DEFS_H +#define WRT_DEFS_H + +typedef enum { + WRT_OK = 0, + WRT_ERROR = -1, +} Status; + +typedef void *(*Allocator)(unsigned long); +typedef void (*DeAllocator)(void *); + +#define DL_MAX_HANDLES 8 +#define DL_MAX_SYMBOLS 16 + +#endif // WRT_DEFS_H diff --git a/utils/log.h b/utils/log.h new file mode 100644 index 0000000..45753d1 --- /dev/null +++ b/utils/log.h @@ -0,0 +1,32 @@ +#ifndef WRT_LOG_H +#define WRT_LOG_H + +#if WRT_DEBUG + #include + + #define __COLOR_RED "\x1B[1;31m" + #define __COLOR_YELLOW "\x1B[1;33m" + #define __COLOR_GREEN "\x1B[1;32m" + #define __COLOR_CYAN "\x1B[1;36m" + #define __COLOR_RESET "\x1B[0m" + + #define _LOG(color, level, fmt, ...) \ + do { \ + fprintf(stdout, color "[" level "]\t(%s:%d):\t" fmt __COLOR_RESET "\n", __FILE__, \ + __LINE__, ##__VA_ARGS__); \ + } while (0) + + #define LOG_ERR(fmt, ...) _LOG(__COLOR_RED, "E", fmt, ##__VA_ARGS__) + #define LOG_WARN(fmt, ...) _LOG(__COLOR_YELLOW, "W", fmt, ##__VA_ARGS__) + #define LOG_INFO(fmt, ...) _LOG(__COLOR_GREEN, "I", fmt, ##__VA_ARGS__) + #define LOG_DBG(fmt, ...) _LOG(__COLOR_CYAN, "D", fmt, ##__VA_ARGS__) + +#else + #define LOG_ERR(fmt, ...) + #define LOG_WARN(fmt, ...) + #define LOG_INFO(fmt, ...) + #define LOG_DBG(fmt, ...) + +#endif + +#endif // WRT_LOG_H diff --git a/wasm.conf b/wasm.conf new file mode 100644 index 0000000..4cf9ed0 --- /dev/null +++ b/wasm.conf @@ -0,0 +1,9 @@ +set(WAMR_BUILD_PLATFORM "linux") +set(WAMR_BUILD_TARGET "X86_64") +set(WAMR_BUILD_INTERP 1) +set(WAMR_BUILD_FAST_INTERP 1) +set(WAMR_BUILD_AOT 1) +set(WAMR_BUILD_LIBC_BUILTIN 1) +set(WAMR_BUILD_LIBC_WASI 1) +set(WAMR_BUILD_SIMD 1) +set(WAMR_ROOT_DIR $ENV{WAMR_ROOT_DIR}) diff --git a/wrt/dl.c b/wrt/dl.c new file mode 100644 index 0000000..24fdc5f --- /dev/null +++ b/wrt/dl.c @@ -0,0 +1,238 @@ +#include +#include +#include + +#include "utils/log.h" +#include "wrt/dl.h" + +DLContext dl_context = {0}; +Allocator dl_malloc = malloc; +DeAllocator dl_free = free; + +#warning "TODO: Permission Check" + +Status dl_init() { + memset(&dl_context, 0, sizeof(DLContext)); + + return WRT_OK; +} + +int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags) { + if (filename == NULL) return WRT_ERROR; + if (filename[0] == '\0') return WRT_ERROR; + + void *handle = dlopen(filename, flags); + if (handle == NULL) { + LOG_ERR("dlopen failed: %s", dlerror()); + return WRT_ERROR; + } + + for (int i = 0; i < DL_MAX_HANDLES; i++) { + if (dl_context.hnd[i].handle == NULL) { + dl_context.hnd[i].handle = handle; + return i + 1; + } + } + + dlclose(handle); + LOG_ERR("dl_open: too many handles"); + return WRT_ERROR; +} + +bool signature_check(const char *signature, int *arg_count, bool *has_ret) { + const static char allowed_chars[] = {'i', 'l', 'f', 'd', 'p'}; + const static int allowed_chars_len = sizeof(allowed_chars) / sizeof(char); + + // tmpl: '(' ch* ')' ch? + + const char *ch = signature; + + // 1. first char must be '(' + if (*ch != '(') return false; + + // 2. check inside paren + *arg_count = 0; + ch++; + bool right_paren = false, illegal = false; + for (; *ch != '\0'; ch++) { + if (*ch == ')') { + right_paren = true; + ch++; + break; + } + + bool found = false; + for (int i = 0; i < allowed_chars_len; i++) { + if (*ch == allowed_chars[i]) { + found = true; + break; + } + } + if (!found) { + illegal = true; + break; + } + + (*arg_count)++; + } + if (!right_paren || illegal) return false; + + // 3. check after paren + *has_ret = false; + if (*ch == '\0') return true; + + *has_ret = true; + bool found = false; + for (int i = 0; i < allowed_chars_len; i++) { + if (*ch == allowed_chars[i]) { + found = true; + break; + } + } + if (!found) return false; + if (*(ch + 1) != '\0') return false; + + return true; +} + +ffi_cif *create_ffi_cif(const char *signature) { + int arg_count; + bool has_ret; + + if (!signature_check(signature, &arg_count, &has_ret)) return NULL; + + ffi_cif *cif = dl_malloc(sizeof(ffi_cif)); + ffi_type **arg_types = dl_malloc(sizeof(ffi_type *) * arg_count); + ffi_type *ret_type; + + const char *ch = signature + 1; + { // extract args + int i = 0; + while (*ch != ')') { + switch (*ch) { + case 'i': arg_types[i++] = &ffi_type_sint; break; + case 'l': arg_types[i++] = &ffi_type_slong; break; + case 'f': arg_types[i++] = &ffi_type_float; break; + case 'd': arg_types[i++] = &ffi_type_double; break; + case 'p': arg_types[i++] = &ffi_type_pointer; break; // TODO: pointer to pointer + default: { + LOG_ERR("Invalid signature: %s", signature); + goto fail; + } + } + ch++; + } + } + + // ch = signature + 2 + arg_count; + ch++; + { // extract ret + switch (*ch) { + case 'i': ret_type = &ffi_type_sint; break; + case 'l': ret_type = &ffi_type_slong; break; + case 'f': ret_type = &ffi_type_float; break; + case 'd': ret_type = &ffi_type_double; break; + case 'p': ret_type = &ffi_type_pointer; break; // TODO: pointer to pointer + case '\0': ret_type = &ffi_type_void; break; + default: { + LOG_ERR("Invalid signature: %s", signature); + goto fail; + } + } + } + + ffi_status status = ffi_prep_cif(cif, FFI_DEFAULT_ABI, arg_count, ret_type, arg_types); + if (status != FFI_OK) { + LOG_ERR("ffi_prep_cif failed: %d", status); + goto fail; + } + + return cif; + +fail: + dl_free(arg_types); + dl_free(cif); + return NULL; +} + +int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char *signature) { + if (handle < 1 || handle > DL_MAX_HANDLES) return WRT_ERROR; + if (symbol == NULL || signature == NULL) return WRT_ERROR; + + void *ptr = dl_context.hnd[handle - 1].handle; + if (ptr == NULL) return WRT_ERROR; + + void *sym = dlsym(ptr, symbol); + if (sym == NULL) { + LOG_ERR("dlsym failed: %s", dlerror()); + return WRT_ERROR; + } + + ffi_cif *cif = create_ffi_cif(signature); + if (cif == NULL) return WRT_ERROR; + + for (int i = 0; i < DL_MAX_SYMBOLS; i++) { + if (dl_context.sym[i].symbol == NULL) { + dl_context.sym[i].backref = handle; + dl_context.sym[i].symbol = sym; + dl_context.sym[i].cif = cif; + return i + 1; + } + } + + LOG_ERR("dl_sym: too many symbols"); + return WRT_ERROR; +} + +void dl_call(wasm_exec_env_t exec_env) { + // int sym = (int)wrt_pop(); + // if (sym < 1 || sym > DL_MAX_SYMBOLS) { + // LOG_ERR("dl_call: invalid symbol"); + // return; + // } + // + // if (dl_context.sym[sym - 1].symbol == NULL) { + // LOG_ERR("dl_call: symbol not found"); + // return; + // } + // + // ffi_cif *cif = dl_context.sym[sym - 1].cif; + // + // int arg_count = cif->nargs; + // void *args[arg_count]; + // for (int i = arg_count - 1; i >= 0; i--) { + // args[i] = (void *)wrt_pop(); + // } + // + // void *ret = NULL; + // if (cif->rtype != &ffi_type_void) { + // ret = dl_malloc(cif->rtype->size); + // } + // + // ffi_call(cif, FFI_FN(dl_context.sym[sym - 1].symbol), ret, args); + // + // if (ret != NULL) { + // wrt_push((int)ret); + // } +} + +Status dl_close(wasm_exec_env_t exec_env, int handle) { + if (handle < 1 || handle > DL_MAX_HANDLES) return WRT_ERROR; + + void *ptr = dl_context.hnd[handle - 1].handle; + if (ptr == NULL) return WRT_ERROR; + + dl_context.hnd[handle - 1].handle = NULL; + for (int i = 0; i < DL_MAX_SYMBOLS; i++) { + if (dl_context.sym[i].backref == handle) { + dl_context.sym[i].backref = 0; + dl_context.sym[i].symbol = NULL; + + dl_free(dl_context.sym[i].cif->arg_types); + dl_free(dl_context.sym[i].cif); + dl_context.sym[i].cif = NULL; + } + } + + return dlclose(ptr) == 0 ? WRT_OK : WRT_ERROR; +} diff --git a/wrt/dl.h b/wrt/dl.h new file mode 100644 index 0000000..27e1564 --- /dev/null +++ b/wrt/dl.h @@ -0,0 +1,27 @@ +#ifndef WRT_DL_H +#define WRT_DL_H + +#include +#include +#include + +#include "utils/defs.h" + +typedef struct { + struct { + void *handle; + } hnd[DL_MAX_HANDLES]; + struct { + unsigned backref; + void *symbol; + ffi_cif *cif; + } sym[DL_MAX_SYMBOLS]; +} DLContext; + +Status dl_init(); +int dl_open(wasm_exec_env_t exec_env, const char *filename, int flags); +int dl_sym(wasm_exec_env_t exec_env, int handle, const char *symbol, const char *signature); +void dl_call(wasm_exec_env_t exec_env); // TODO +Status dl_close(wasm_exec_env_t exec_env, int handle); + +#endif // WRT_DL_H diff --git a/wrt/tinymap.c b/wrt/tinymap.c new file mode 100644 index 0000000..6a4985d --- /dev/null +++ b/wrt/tinymap.c @@ -0,0 +1,130 @@ +#include +#include + +#include "utils/log.h" +#include "wrt/tinymap.h" + +#define FOR(i) for (unsigned i = map->head[hash]; i; i = map->node[i].next) + +static inline unsigned map_hash(int key) { return (key % BUK_SIZE + BUK_SIZE) % BUK_SIZE; } + +void map_init(Map *map) { + map->count = 0; + memset(map->head, 0, sizeof(map->head)); + memset(map->node, 0, sizeof(map->node)); +} + +void *map_get(Map *map, int key) { + if (key == 0) return NULL; + + unsigned hash = map_hash(key); + FOR(i) { + if (map->node[i].key == key) return map->node[i].value; + } + return NULL; +} + +bool map_set(Map *map, int key, void *value) { + if (key == 0) return false; + + unsigned hash = map_hash(key); + FOR(i) { + if (map->node[i].key == key) { + LOG_DBG("map_set: upd key=%d, val=0x%llx, pos=%d", key, (unsigned long long)value, i); + map->node[i].value = value; + return true; + } + } + + if (map->count >= NOD_SIZE) { + LOG_DBG("map_set: nod full, compact"); + map_compact(map); + if (map->count == NOD_SIZE) return false; + } + + map->node[++map->count] = (MapNode){key, value, map->head[hash]}; + map->head[hash] = map->count; + LOG_DBG("map_set: ins key=%d, val=0x%llx, pos=%d", key, (unsigned long long)value, map->count); + + return true; +} + +void map_delete(Map *map, int key) { + if (key == 0) return; + + unsigned hash = map_hash(key); + FOR(i) { + if (map->node[i].key == key) { + LOG_DBG("map_delete: del key=%d, pos=%d", key, i); + map->node[i].key = 0; + return; + } + } +} + +void map_compact(Map *map) { + LOG_DBG("map_compact: before count=%d", map->count); + + { + // compact map->node + // i = last empty node, j = current scanning node + // node[0] is undefined + unsigned i = 1, j = 2; + for (; j <= NOD_SIZE;) { + if (map->node[i].key) { + i++, j++; + continue; + } + + if (map->node[j].key) { + LOG_DBG("map_compact: move node[%d] to node[%d]", j, i); + + map->node[i] = map->node[j]; + map->node[j].key = 0; + + i++, j++; + continue; + } + + j++; + } + + map->count = i - 1; + + LOG_DBG("map_compact: after count=%d", map->count); + } + + { + // rebuild map->head + for (unsigned i = 0; i <= NOD_SIZE; i++) map->node[i].next = 0; + memset(map->head, 0, sizeof(map->head)); + for (unsigned i = 1; i <= NOD_SIZE; i++) { + if (!map->node[i].key) break; + + unsigned hash = map_hash(map->node[i].key); + + if (map->head[hash] == 0) { + map->head[hash] = i; + } else { + unsigned j = map->head[hash]; + map->head[hash] = i; + map->node[i].next = j; + } + } + } +} + +void map_dbg(Map *map) { + for (int hash = 0; hash < BUK_SIZE; hash++) { + printf("[+] BUK[%2d]:\n", hash); + int spaces = 5; + FOR(i) { + for (int j = 0; j < spaces; j++) putchar(' '); + printf("└─ NOD[" __COLOR_GREEN "%2d" __COLOR_RESET "]: key=" __COLOR_YELLOW + "%d" __COLOR_RESET ", val=" __COLOR_CYAN "0x%llx" __COLOR_RESET + ", nxt=" __COLOR_RED "%d" __COLOR_RESET "\n", + i, map->node[i].key, (unsigned long long)map->node[i].value, map->node[i].next); + spaces += 4; + } + } +} diff --git a/wrt/tinymap.h b/wrt/tinymap.h new file mode 100644 index 0000000..0537de5 --- /dev/null +++ b/wrt/tinymap.h @@ -0,0 +1,32 @@ +#ifndef WRT_TINYMAP_H +#define WRT_TINYMAP_H + +#include + +#ifndef BUK_SIZE + #define BUK_SIZE 8 +#endif + +#ifndef NOD_SIZE + #define NOD_SIZE 32 +#endif + +typedef struct { + int key; + void *value; + unsigned next; +} MapNode; + +typedef struct { + MapNode node[NOD_SIZE + 1]; + unsigned head[BUK_SIZE]; + unsigned count; +} Map; + +void map_init(Map *map); +void *map_get(Map *map, int key); +bool map_set(Map *map, int key, void *value); +void map_delete(Map *map, int key); +void map_compact(Map *map); + +#endif // WRT_TINYMAP_H diff --git a/wrt/wrt.c b/wrt/wrt.c new file mode 100644 index 0000000..1789002 --- /dev/null +++ b/wrt/wrt.c @@ -0,0 +1,167 @@ +#include + +#include "utils/log.h" +#include "wrt.h" + +Status wrt_platform_init(WRTContext *context, Allocator allocator, DeAllocator deAllocator) { + context->platform.malloc = allocator; + context->platform.free = deAllocator; + + return WRT_OK; +} + +Status wrt_mem_init(WRTContext *context, uint32_t heap_buf_size, uint32_t error_buf_size) { + context->mem.heap_buf = NULL; + context->mem.error_buf = NULL; + + context->mem.heap_buf = context->platform.malloc(sizeof(char) * heap_buf_size); + context->mem.heap_buf_size = heap_buf_size; + if (context->mem.heap_buf == NULL) { + LOG_ERR("malloc heap_buf failed"); + goto fail2; + } + + context->mem.error_buf = context->platform.malloc(sizeof(char) * error_buf_size); + context->mem.error_buf_size = error_buf_size; + if (context->mem.error_buf == NULL) { + LOG_ERR("malloc error_buf failed"); + goto fail1; + } + + return WRT_OK; + +fail1: + context->mem.error_buf = NULL; + context->mem.error_buf_size = 0; + context->platform.free(context->mem.heap_buf); +fail2: + context->mem.heap_buf = NULL; + context->mem.heap_buf_size = 0; + + return WRT_ERROR; +} + +Status wrt_program_init(WRTContext *context, uint8_t *wasm_buffer, uint32_t wasm_buffer_size, + NativeSymbol *native_symbols, uint32_t native_symbols_size) { + context->program.wasm_buffer = wasm_buffer; + context->program.wasm_buffer_size = wasm_buffer_size; + context->program.native_symbols = native_symbols; + context->program.native_symbols_size = native_symbols_size; + + return WRT_OK; +} + +Status wrt_wamr_init(WRTContext *context, char *entry_func, uint32_t stack_size, + uint32_t heap_size) { + RuntimeInitArgs init_args; + memset(&init_args, 0, sizeof(RuntimeInitArgs)); + + // runtime memory + init_args.mem_alloc_type = Alloc_With_Pool; + init_args.mem_alloc_option.pool.heap_buf = context->mem.heap_buf; + init_args.mem_alloc_option.pool.heap_size = context->mem.heap_buf_size; + + // runtime functions + init_args.n_native_symbols = context->program.native_symbols_size; + init_args.native_module_name = "env"; + init_args.native_symbols = context->program.native_symbols; + + // init environment + if (!wasm_runtime_full_init(&init_args)) { + LOG_ERR("Init runtime environment failed."); + return WRT_ERROR; + } + + // init dl + dl_init(); + + // register dl functions + static NativeSymbol native_symbols[] = { + EXPORT_WASM_API_WITH_SIG(dl_open, "(si)i"), + EXPORT_WASM_API_WITH_SIG(dl_sym, "(iss)i"), + EXPORT_WASM_API_WITH_SIG(dl_call, "(i)i"), // TODO + EXPORT_WASM_API_WITH_SIG(dl_close, "(i)i"), + }; + wasm_runtime_register_natives("env", native_symbols, + sizeof(native_symbols) / sizeof(NativeSymbol)); + + // load module + context->wamr.module = + wasm_runtime_load(context->program.wasm_buffer, context->program.wasm_buffer_size, + context->mem.error_buf, context->mem.error_buf_size); + if (!context->wamr.module) { + LOG_ERR("Load wasm module failed. error: %s", context->mem.error_buf); + goto fail; + } + + // instantiate module + context->wamr.module_inst = + wasm_runtime_instantiate(context->wamr.module, stack_size, heap_size, + context->mem.error_buf, sizeof(context->mem.error_buf)); + if (!context->wamr.module_inst) { + LOG_ERR("Instantiate wasm module failed. error: %s", context->mem.error_buf); + goto fail; + } + + // create env + context->wamr.exec_env = wasm_runtime_create_exec_env(context->wamr.module_inst, stack_size); + if (!context->wamr.exec_env) { + LOG_ERR("Create wasm execution environment failed."); + goto fail; + } + + // find entry function + if (!(context->wamr.entry_func = + wasm_runtime_lookup_function(context->wamr.module_inst, entry_func, NULL))) { + LOG_ERR("The wasm function %s wasm function is not found.", entry_func); + goto fail; + } + + return WRT_OK; + +fail: + if (context->wamr.exec_env) wasm_runtime_destroy_exec_env(context->wamr.exec_env); + if (context->wamr.module_inst) { + wasm_runtime_deinstantiate(context->wamr.module_inst); + } + if (context->wamr.module) wasm_runtime_unload(context->wamr.module); + wasm_runtime_destroy(); + + return WRT_ERROR; +} + +Status wrt_free(WRTContext *context) { + if (context->wamr.exec_env) { + wasm_runtime_destroy_exec_env(context->wamr.exec_env); + context->wamr.exec_env = NULL; + } + + if (context->wamr.module_inst) { + wasm_runtime_deinstantiate(context->wamr.module_inst); + context->wamr.module_inst = NULL; + } + + if (context->wamr.module) { + wasm_runtime_unload(context->wamr.module); + context->wamr.module = NULL; + } + + wasm_runtime_destroy(); + + context->platform.free(context->mem.error_buf); + context->mem.error_buf_size = 0; + context->platform.free(context->mem.heap_buf); + context->mem.heap_buf_size = 0; + + return WRT_OK; +} + +Status wrt_run(WRTContext *context) { + // run wasm + if (!wasm_runtime_call_wasm(context->wamr.exec_env, context->wamr.entry_func, 0, NULL)) { + LOG_ERR("Call wasm function failed. error: %s", context->mem.error_buf); + return WRT_ERROR; + } + + return WRT_OK; +} diff --git a/wrt/wrt.h b/wrt/wrt.h new file mode 100644 index 0000000..d082f5b --- /dev/null +++ b/wrt/wrt.h @@ -0,0 +1,48 @@ +#ifndef WRT_WRT_H +#define WRT_WRT_H + +#include + +#include "utils/defs.h" +#include "wrt/dl.h" + +typedef struct { + struct { + Allocator malloc; + DeAllocator free; + } platform; + struct { + wasm_module_t module; + wasm_module_inst_t module_inst; + wasm_exec_env_t exec_env; + wasm_function_inst_t entry_func; + } wamr; + struct { + uint8_t *heap_buf; + uint32_t heap_buf_size; + char *error_buf; + uint32_t error_buf_size; + } mem; + struct { + uint8_t *wasm_buffer; + uint32_t wasm_buffer_size; + NativeSymbol *native_symbols; + uint32_t native_symbols_size; + } program; +} WRTContext; + +Status wrt_platform_init(WRTContext *context, Allocator allocator, DeAllocator deAllocator); + +Status wrt_mem_init(WRTContext *context, uint32_t heap_buf_size, uint32_t error_buf_size); + +Status wrt_program_init(WRTContext *context, uint8_t *wasm_buffer, uint32_t wasm_buffer_size, + NativeSymbol *native_symbols, uint32_t native_symbols_size); + +Status wrt_wamr_init(WRTContext *context, char *entry_func, uint32_t stack_size, + uint32_t heap_size); + +Status wrt_free(WRTContext *context); + +Status wrt_run(WRTContext *context); + +#endif // WRT_WRT_H