summaryrefslogtreecommitdiffhomepage
path: root/src/wasm/nxt_rt_wamr.c
diff options
context:
space:
mode:
authorAndrew Clayton <a.clayton@nginx.com>2023-10-10 20:54:39 +0100
committerAndrew Clayton <a.clayton@nginx.com>2023-10-10 20:54:39 +0100
commit8f6449f4e0152d9644b807cf2208e3734a0f8b4d (patch)
tree266081fadb3ca5131e22452e315f6a6dd544d32e /src/wasm/nxt_rt_wamr.c
parent01d185cb52af8879aeeab04765eff439feec664c (diff)
downloadunit-wasm.tar.gz
unit-wasm.tar.bz2
[WIP] Wasm: Add a WAMR based language modulewasm
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
Diffstat (limited to 'src/wasm/nxt_rt_wamr.c')
-rw-r--r--src/wasm/nxt_rt_wamr.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/src/wasm/nxt_rt_wamr.c b/src/wasm/nxt_rt_wamr.c
new file mode 100644
index 00000000..d33965ba
--- /dev/null
+++ b/src/wasm/nxt_rt_wamr.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) Andrew Clayton
+ * Copyright (C) F5, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <wasm_export.h>
+
+#include "nxt_wasm.h"
+
+
+typedef struct nxt_wamr_ctx_s nxt_wamr_ctx_t;
+
+struct nxt_wamr_ctx_s {
+ uint8_t *mod_buf;
+ wasm_module_t module;
+ wasm_module_inst_t module_inst;
+ wasm_exec_env_t exec_env;
+};
+
+static nxt_wamr_ctx_t nxt_wamr_ctx;
+
+
+static void
+nxt_wamr_err_msg(const char *fmt, ...)
+{
+ char dstr[20];
+ time_t t;
+ va_list args;
+ struct tm *tmp;
+
+ t = time(NULL);
+ tmp = localtime(&t);
+ if (tmp == NULL) {
+ return;
+ }
+
+ strftime(dstr, sizeof(dstr), "%Y/%m/%d %T", tmp);
+
+ fprintf(stderr, "%s [WAMR ERROR] %d ", dstr, getpid());
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+}
+
+
+static uint32_t
+nxt_wasm_get_init_mem_size(wasm_exec_env_t exec_env)
+{
+ return NXT_WASM_MEM_SIZE;
+}
+
+
+static void
+nxt_wasm_response_end(wasm_exec_env_t exec_env)
+{
+ nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
+
+ nxt_wasm_do_response_end(ctx);
+}
+
+
+static void
+nxt_wasm_send_response(wasm_exec_env_t exec_env, uint32_t offset)
+{
+ nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
+
+ nxt_wasm_do_send_response(ctx, offset);
+}
+
+
+static void
+nxt_wasm_send_headers(wasm_exec_env_t exec_env, uint32_t offset)
+{
+ nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
+
+ nxt_wasm_do_send_headers(ctx, offset);
+}
+
+
+static void
+nxt_wasm_set_resp_status(wasm_exec_env_t exec_env, uint16_t status)
+{
+ nxt_wasm_ctx_t *ctx = wasm_runtime_get_user_data(exec_env);
+
+ ctx->status = status;
+}
+
+
+static void
+nxt_wamr_execute_hook(const nxt_wasm_ctx_t *ctx, nxt_wasm_fh_t hook)
+{
+ bool ok;
+ const char *name = ctx->fh[hook].func_name;
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+ const nxt_wasm_func_t *func = ctx->fh[hook].func;
+
+ if (name == NULL) {
+ return;
+ }
+
+ ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
+ 0, NULL, 0, NULL);
+ if (!ok) {
+ nxt_wamr_err_msg("failed to call hook function [%s]. (%s)", name,
+ wasm_runtime_get_exception(rt_ctx->module_inst));
+ }
+}
+
+
+static int
+nxt_wamr_execute_request(const nxt_wasm_ctx_t *ctx)
+{
+ bool ok;
+ uint32_t i = 0;
+ wasm_val_t args[1] = { };
+ wasm_val_t results[1] = { };
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+ const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_REQUEST].func;
+
+ args[i].kind = WASM_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
+ 1, results, i, args);
+ if (!ok) {
+ nxt_wamr_err_msg("Failed to call function [->wasm_request_handler]. "
+ "(%s)",
+ wasm_runtime_get_exception(rt_ctx->module_inst));
+ return -1;
+ }
+
+ return results[0].of.i32;
+}
+
+
+static bool
+nxt_wamr_set_function_imports(void)
+{
+ int nr_funcs;
+ bool ok;
+
+ static NativeSymbol import_functions[] = {
+ EXPORT_WASM_API_WITH_SIG(nxt_wasm_get_init_mem_size, "()i"),
+ EXPORT_WASM_API_WITH_SIG(nxt_wasm_response_end, "()"),
+ EXPORT_WASM_API_WITH_SIG(nxt_wasm_send_response, "(i)"),
+ EXPORT_WASM_API_WITH_SIG(nxt_wasm_send_headers, "(i)"),
+ EXPORT_WASM_API_WITH_SIG(nxt_wasm_set_resp_status, "(i)"),
+ };
+
+ nr_funcs = sizeof(import_functions) / sizeof(NativeSymbol);
+
+ ok = wasm_runtime_register_natives("env", import_functions, nr_funcs);
+ if (!ok) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static int
+nxt_wamr_get_function_exports(nxt_wasm_ctx_t *ctx)
+{
+ int i;
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+
+ for (i = 0; i < NXT_WASM_FH_NR; i++) {
+ if (ctx->fh[i].func_name == NULL) {
+ continue;
+ }
+
+ ctx->fh[i].func = wasm_runtime_lookup_function(rt_ctx->module_inst,
+ ctx->fh[i].func_name,
+ NULL);
+ if (ctx->fh[i].func == NULL) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+nxt_wamr_wasi_init(const nxt_wasm_ctx_t *ctx)
+{
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+
+ wasm_runtime_set_wasi_args(rt_ctx->module,
+ (const char **)ctx->dirs, ctx->nr_dirs,
+ NULL, 0, NULL, 0, NULL, 0);
+}
+
+
+static int
+nxt_wamr_init_memory(nxt_wasm_ctx_t *ctx)
+{
+ bool ok;
+ uint32_t i = 0;
+ wasm_val_t args[1] = { };
+ wasm_val_t results[1] = { };
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+ const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_MALLOC].func;
+
+ args[i].kind = WASM_I32;
+ args[i++].of.i32 = NXT_WASM_MEM_SIZE + NXT_WASM_PAGE_SIZE;
+
+ ok = wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
+ 1, results, i, args);
+ if (!ok) {
+ return -1;
+ }
+
+ ctx->baddr_off = results[0].of.i32;
+ ctx->baddr = wasm_runtime_addr_app_to_native(rt_ctx->module_inst,
+ ctx->baddr_off);
+
+ return 0;
+}
+
+
+static int
+nxt_wamr_init(nxt_wasm_ctx_t *ctx)
+{
+ int err;
+ bool ok;
+ char error_buf[128];
+ FILE *fp;
+ size_t file_size;
+ uint32_t stack_size = 16384, heap_size = 16384;
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+
+ ok = wasm_runtime_init();
+ if (!ok) {
+ nxt_wamr_err_msg("wasm_runtime_init() failed");
+ return -1;
+ }
+
+ ok = nxt_wamr_set_function_imports();
+ if (!ok) {
+ nxt_wamr_err_msg("nxt_wamr_set_function_imports() failed");
+ return -1;
+ }
+
+ fp = fopen(ctx->module_path, "r");
+ if (!fp) {
+ nxt_wamr_err_msg("fopen(\"%s\") failed", ctx->module_path);
+ return -1;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ file_size = ftell(fp);
+ rt_ctx->mod_buf = malloc(file_size);
+ fseek(fp, 0L, SEEK_SET);
+ if (fread(rt_ctx->mod_buf, file_size, 1, fp) != 1) {
+ nxt_wamr_err_msg("fread(\"%s\") failed", ctx->module_path);
+ return -1;
+ }
+ fclose(fp);
+
+ rt_ctx->module = wasm_runtime_load(rt_ctx->mod_buf, file_size, error_buf,
+ sizeof(error_buf));
+ if (rt_ctx->module == NULL) {
+ nxt_wamr_err_msg("wasm_runtime_load() failed");
+ return -1;
+ }
+
+ nxt_wamr_wasi_init(ctx);
+
+ rt_ctx->module_inst = wasm_runtime_instantiate(rt_ctx->module, stack_size,
+ heap_size, error_buf,
+ sizeof(error_buf));
+ if (rt_ctx->module_inst == NULL) {
+ nxt_wamr_err_msg("wasm_runtime_instantiate() failed");
+ return -1;
+ }
+
+ rt_ctx->exec_env = wasm_runtime_create_exec_env(rt_ctx->module_inst,
+ stack_size);
+ if (rt_ctx->exec_env == NULL) {
+ nxt_wamr_err_msg("wasm_runtime_create_exec_env() failed");
+ return -1;
+ }
+ wasm_runtime_set_user_data(rt_ctx->exec_env, ctx);
+
+ err = nxt_wamr_get_function_exports(ctx);
+ if (err) {
+ nxt_wamr_err_msg("nxt_wamr_get_function_exports() failed");
+ return -1;
+ }
+
+ err = nxt_wamr_init_memory(ctx);
+ if (err) {
+ nxt_wamr_err_msg("nxt_wamr_init_memory() failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+nxt_wamr_destroy(const nxt_wasm_ctx_t *ctx)
+{
+ uint32_t i = 0;
+ wasm_val_t args[1] = { };
+ nxt_wamr_ctx_t *rt_ctx = &nxt_wamr_ctx;
+ const nxt_wasm_func_t *func = ctx->fh[NXT_WASM_FH_FREE].func;
+
+ args[i].kind = WASM_I32;
+ args[i++].of.i32 = ctx->baddr_off;
+
+ wasm_runtime_call_wasm_a(rt_ctx->exec_env, (nxt_wasm_func_t)func,
+ 0, NULL, i, args);
+
+ wasm_runtime_destroy_exec_env(rt_ctx->exec_env);
+ wasm_runtime_deinstantiate(rt_ctx->module_inst);
+ wasm_runtime_unload(rt_ctx->module);
+ free(rt_ctx->mod_buf);
+ wasm_runtime_destroy();
+}
+
+
+const nxt_wasm_operations_t nxt_wasm_ops = {
+ .init = nxt_wamr_init,
+ .destroy = nxt_wamr_destroy,
+ .exec_request = nxt_wamr_execute_request,
+ .exec_hook = nxt_wamr_execute_hook,
+};