summaryrefslogblamecommitdiffhomepage
path: root/src/wasm/nxt_rt_wamr.c
blob: d33965bab7cb8e695ea6503483c2f7935cec83db (plain) (tree)

















































































































































































































































































































































                                                                              
/*
 * 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,
};