Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ libcrun_SOURCES = src/libcrun/utils.c \
src/libcrun/handlers/wasmedge.c \
src/libcrun/handlers/wasmer.c \
src/libcrun/handlers/wasmtime.c \
src/libcrun/handlers/wamr.c \
src/libcrun/intelrdt.c \
src/libcrun/io_priority.c \
src/libcrun/linux.c \
Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ dnl include support for wasmedge (EXPERIMENTAL)
AC_ARG_WITH([wasmedge], AS_HELP_STRING([--with-wasmedge], [build with WasmEdge support]))
AS_IF([test "x$with_wasmedge" = "xyes"], AC_CHECK_HEADERS([wasmedge/wasmedge.h], AC_DEFINE([HAVE_WASMEDGE], 1, [Define if WasmEdge is available]), [AC_MSG_ERROR([*** Missing wasmedge headers])]))

dnl include support for wamr (EXPERIMENTAL)
AC_ARG_WITH([wamr], AS_HELP_STRING([--with-wamr], [build with WAMR support]))
AS_IF([test "x$with_wamr" = "xyes"], AC_CHECK_HEADERS([wasm_export.h], AC_DEFINE([HAVE_WAMR], 1, [Define if WAMR is available]), [AC_MSG_ERROR([*** Missing WAMR headers])]))

dnl include support for libkrun (EXPERIMENTAL)
AC_ARG_WITH([libkrun], AS_HELP_STRING([--with-libkrun], [build with libkrun support]))
AS_IF([test "x$with_libkrun" = "xyes"], AC_CHECK_HEADERS([libkrun.h], AC_DEFINE([HAVE_LIBKRUN], 1, [Define if libkrun is available]), [AC_MSG_ERROR([*** Missing libkrun headers])]))
Expand Down
7 changes: 4 additions & 3 deletions docs/wasm-wasi-on-kubernetes.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Running wasi workload natively on kubernetes using crun

Crun natively supports running wasm/wasi workload on using `wasmedge`, `wasmer` and `wasmtime`.
Each one of them (`wasmedge`, `wasmer` and `wasmtime`) comes with their own set of unique features.
For instance `wasmer` can compile your `.wat` on the fly. Similarly `wasmedge` has its own perks.
Crun natively supports running wasm/wasi workload on using `wasmedge`, `wasmer`, `wasmtime` and `wamr`.
Each one of them (`wasmedge`, `wasmer`, `wasmtime` and `wamr`) comes with their own set of unique features.
For instance `wasmer` can compile your `.wat` on the fly. Similarly `wasmedge` has its own perks. `wamr` has a layered JIT architecture which can tier up during runtime.
Crun can support only one of them at a time. Please build crun with whatever runtime suits you the best.

#### How does crun detects if is a wasm workload ?
Expand Down Expand Up @@ -31,6 +31,7 @@ So spec generated by CRI implementation must contain annotation something like.

* Following features works completely out if the box once `cri-o` is using `crun` built with `wasm` support.
* Configure `cri-o` to use `crun` instead of `runc` by editing config at `/etc/crio/crio.conf` read more about it here https://docs.openshift.com/container-platform/3.11/crio/crio_runtime.html#configure-crio-use-crio-engine
* As of `cri-o` version `1.31` it defaults to `crun`, but the bundled `crun` may not have been built with `wasm` support.
* Restart `cri-o` by `sudo systemctl restart crio`
* `cri-o` automatically propagates pod annotations to container spec. So we don't need to do anything.

Expand Down
6 changes: 6 additions & 0 deletions src/libcrun/custom-handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ extern struct custom_handler_s handler_wasmedge;
#if HAVE_DLOPEN && HAVE_WASMER
extern struct custom_handler_s handler_wasmer;
#endif
#if HAVE_DLOPEN && HAVE_WAMR
extern struct custom_handler_s handler_wamr;
#endif
#if HAVE_DLOPEN && HAVE_MONO
extern struct custom_handler_s handler_mono;
#endif
Expand All @@ -65,6 +68,9 @@ static struct custom_handler_s *static_handlers[] = {
#if HAVE_DLOPEN && HAVE_WASMTIME
&handler_wasmtime,
#endif
#if HAVE_DLOPEN && HAVE_WAMR
&handler_wamr,
#endif
#if HAVE_DLOPEN && HAVE_MONO
&handler_mono,
#endif
Expand Down
247 changes: 247 additions & 0 deletions src/libcrun/handlers/wamr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/*
* crun - OCI runtime written in C
*
* Copyright (C) 2017, 2018, 2019, 2020, 2021 Giuseppe Scrivano <giuseppe@scrivano.org>
* crun is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* crun is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with crun. If not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE

#include <config.h>
#include "../custom-handler.h"
#include "../container.h"
#include "../utils.h"
#include "../linux.h"
#include "handler-utils.h"
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_DLOPEN
# include <dlfcn.h>
#endif

#ifdef HAVE_WAMR
# include <wasm_export.h>
#endif

#if HAVE_DLOPEN && HAVE_WAMR

static int
libwamr_load (void **cookie, libcrun_error_t *err)
{
void *handle;

handle = dlopen ("libiwasm.so", RTLD_NOW);
if (handle == NULL)
return crun_make_error (err, 0, "could not load `libiwasm.so`: `%s`", dlerror ());
*cookie = handle;

return 0;
}

static int
libwamr_unload (void *cookie, libcrun_error_t *err)
{
int r;

if (cookie)
{
r = dlclose (cookie);
if (UNLIKELY (r < 0))
return crun_make_error (err, 0, "could not unload handle: `%s`", dlerror ());
}
return 0;
}

static int
libwamr_exec (void *cookie, __attribute__ ((unused)) libcrun_container_t *container, const char *pathname, char *const argv[])
{
// load symbols from the shared library libiwasm.so
bool (*wasm_runtime_init) ();
RuntimeInitArgs init_args;
bool (*wasm_runtime_full_init) (RuntimeInitArgs *init_args);
wasm_module_t module;
wasm_module_t (*wasm_runtime_load) (uint8_t *buf, uint32_t size, char *error_buf, uint32_t error_buf_size);
wasm_module_inst_t module_inst;
wasm_module_inst_t (*wasm_runtime_instantiate) (const wasm_module_t module, uint32_t default_stack_size, uint32_t host_managed_heap_size, char *error_buf, uint32_t error_buf_size);
wasm_function_inst_t func;
wasm_function_inst_t (*wasm_runtime_lookup_function) (wasm_module_inst_t const module_inst, const char *name);
wasm_exec_env_t exec_env;
wasm_exec_env_t (*wasm_runtime_create_exec_env) (wasm_module_inst_t module_inst, uint32_t stack_size);
bool (*wasm_runtime_call_wasm) (wasm_exec_env_t exec_env, wasm_function_inst_t function, uint32_t argc, uint32_t argv[]);
const char *(*wasm_runtime_get_exception) (wasm_module_inst_t module_inst);
void (*wasm_runtime_set_exception) (wasm_module_inst_t module_inst, const char *exception);
void (*wasm_runtime_clear_exception) (wasm_module_inst_t module_inst);
void (*wasm_runtime_destroy_exec_env) (wasm_exec_env_t exec_env);
void (*wasm_runtime_deinstantiate) (wasm_module_inst_t module_inst);
void (*wasm_runtime_unload) (wasm_module_t module);
void (*wasm_runtime_destroy) ();
uint32_t (*wasm_runtime_get_wasi_exit_code) (wasm_module_inst_t module_inst);
bool (*wasm_application_execute_main) (wasm_module_inst_t module_inst, int32_t argc, char *argv[]);
void (*wasm_runtime_set_wasi_args) (wasm_module_t module, const char *dir_list[], uint32_t dir_count, const char *map_dir_list[], uint32_t map_dir_count, const char *env[], uint32_t env_count, char *argv[], int argc);

wasm_runtime_init = dlsym (cookie, "wasm_runtime_init");
wasm_runtime_full_init = dlsym (cookie, "wasm_runtime_full_init");
wasm_runtime_load = dlsym (cookie, "wasm_runtime_load");
wasm_runtime_instantiate = dlsym (cookie, "wasm_runtime_instantiate");
wasm_runtime_lookup_function = dlsym (cookie, "wasm_runtime_lookup_function");
wasm_runtime_create_exec_env = dlsym (cookie, "wasm_runtime_create_exec_env");
wasm_runtime_call_wasm = dlsym (cookie, "wasm_runtime_call_wasm");
wasm_runtime_get_exception = dlsym (cookie, "wasm_runtime_get_exception");
wasm_runtime_set_exception = dlsym (cookie, "wasm_runtime_set_exception");
wasm_runtime_clear_exception = dlsym (cookie, "wasm_runtime_clear_exception");
wasm_runtime_destroy_exec_env = dlsym (cookie, "wasm_runtime_destroy_exec_env");
wasm_runtime_deinstantiate = dlsym (cookie, "wasm_runtime_deinstantiate");
wasm_runtime_unload = dlsym (cookie, "wasm_runtime_unload");
wasm_runtime_destroy = dlsym (cookie, "wasm_runtime_destroy");
wasm_runtime_get_wasi_exit_code = dlsym (cookie, "wasm_runtime_get_wasi_exit_code");
wasm_application_execute_main = dlsym (cookie, "wasm_application_execute_main");
wasm_runtime_set_wasi_args = dlsym (cookie, "wasm_runtime_set_wasi_args");

if (wasm_runtime_init == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_init symbol in `libiwasm.so`");
if (wasm_runtime_full_init == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_full_init symbol in `libiwasm.so`");
if (wasm_runtime_load == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_load symbol in `libiwasm.so`");
if (wasm_runtime_instantiate == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_instantiate symbol in `libiwasm.so`");
if (wasm_runtime_lookup_function == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_lookup_function symbol in `libiwasm.so`");
if (wasm_runtime_create_exec_env == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_create_exec_env symbol in `libiwasm.so`");
if (wasm_runtime_call_wasm == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_call_wasm symbol in `libiwasm.so`");
if (wasm_runtime_get_exception == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_get_exception symbol in `libiwasm.so`");
if (wasm_runtime_set_exception == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_set_exception symbol in `libiwasm.so`");
if (wasm_runtime_clear_exception == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_clear_exception symbol in `libiwasm.so`");
if (wasm_runtime_destroy_exec_env == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_destroy_exec_env symbol in `libiwasm.so`");
if (wasm_runtime_deinstantiate == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_deinstantiate symbol in `libiwasm.so`");
if (wasm_runtime_unload == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_unload symbol in `libiwasm.so`");
if (wasm_runtime_destroy == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_destroy symbol in `libiwasm.so`");
if (wasm_runtime_get_wasi_exit_code == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_get_wasi_exit_code symbol in `libiwasm.so`");
if (wasm_application_execute_main == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_application_execute_main symbol in `libiwasm.so`");
if (wasm_runtime_set_wasi_args == NULL)
error (EXIT_FAILURE, 0, "could not find wasm_runtime_set_wasi_args symbol in `libiwasm.so`");

int ret;
const char *exception;
cleanup_free char *buffer = NULL;
char error_buf[128];
size_t buffer_size;
uint32_t stack_size = 8096, heap_size = 0;
libcrun_error_t tmp_err = NULL;
const char *wasi_proc_exit_exception = "wasi proc exit";
const char *wasi_addr_pool[2] = { "0.0.0.0/0", "::/0" };
const char *wasi_ns_lookup_pool[1] = { "*" };

const char *dirs[2] = { "/", "." };
char **container_env = container->container_def->process->env;
size_t env_count = container->container_def->process->env_len;

int arg_count = 0;
char *const *arg;
for (arg = argv; *arg != NULL; ++arg)
arg_count++;

// initialize the wasm runtime by default configurations
if (! wasm_runtime_init ())
error (EXIT_FAILURE, 0, "Failed to initialize the wasm runtime");

// read WASM file into a memory buffer
ret = read_all_file (pathname, &buffer, &buffer_size, &tmp_err);
if (UNLIKELY (ret < 0))
{
crun_error_release (&tmp_err);
error (EXIT_FAILURE, 0, "Failed to read file");
}

if (UNLIKELY (buffer_size > UINT32_MAX))
error (EXIT_FAILURE, 0, "File size is too large");

// parse the WASM file from buffer and create a WASM module
module = wasm_runtime_load (buffer, buffer_size, error_buf, sizeof (error_buf));
if (! module)
error (EXIT_FAILURE, 0, "Failed to load WASM file");

// instantiate the WASI environment
wasm_runtime_set_wasi_args (module, dirs, 1, NULL, 0, (const char **) container_env, env_count, (char **) argv, arg_count);

// enable the WASI socket api
wasm_runtime_set_wasi_addr_pool (module, wasi_addr_pool, 2);
wasm_runtime_set_wasi_ns_lookup_pool (module, wasi_ns_lookup_pool, 1);

// create an instance of the WASM module (WASM linear memory is ready)
module_inst = wasm_runtime_instantiate (module, stack_size, heap_size, error_buf, sizeof (error_buf));
if (! module_inst)
error (EXIT_FAILURE, 0, "Failed to instantiate the WASM module");

// look up a WASM function by its name (The function signature can NULL here)
func = wasm_runtime_lookup_function (module_inst, "_start");
if (! func)
error (EXIT_FAILURE, 0, "Failed to look up the WASM function");

// create an execution environment to execute the WASM functions
exec_env = wasm_runtime_create_exec_env (module_inst, stack_size);
if (! exec_env)
error (EXIT_FAILURE, 0, "Failed to create the execution environment");

// call the WASM function
ret = wasm_runtime_call_wasm (exec_env, func, 0, NULL);
if (ret)
wasm_runtime_set_exception (module_inst, wasi_proc_exit_exception);
exception = wasm_runtime_get_exception (module_inst);
if (! strstr (exception, wasi_proc_exit_exception))
error (EXIT_FAILURE, 0, "Failed to call the WASM function");
wasm_runtime_clear_exception (module_inst);

wasm_runtime_destroy_exec_env (exec_env);
wasm_runtime_deinstantiate (module_inst);
wasm_runtime_unload (module);
wasm_runtime_destroy ();

exit (EXIT_SUCCESS);
}

static int
libwamr_can_handle_container (libcrun_container_t *container, libcrun_error_t *err)
{
return wasm_can_handle_container (container, err);
}

struct custom_handler_s handler_wamr = {
.name = "wamr",
.alias = "wasm",
.feature_string = "WASM:wamr",
.load = libwamr_load,
.unload = libwamr_unload,
.run_func = libwamr_exec,
.can_handle_container = libwamr_can_handle_container,
};

#endif
Loading