aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-09-07 00:04:38 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-11-06 14:38:40 +0100
commit6d9d80362f9932bbc87e162b8ef7df06c73e27e1 (patch)
treee3e228c63fe26f07503f226de7fb5086b3dc2286 /examples
First commit
Diffstat (limited to 'examples')
-rw-r--r--examples/CMakeLists.txt10
-rw-r--r--examples/example.wasmbin0 -> 26749 bytes
-rw-r--r--examples/file.c659
-rw-r--r--examples/minimal.c136
-rw-r--r--examples/proc_exit.c484
5 files changed, 1289 insertions, 0 deletions
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..c182e37
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,10 @@
+function(nw_example example)
+ add_executable(nw_${example} ${example}.c)
+ target_link_libraries(nw_${example} PRIVATE ${PROJECT_NAME})
+ install(TARGETS nw_${example})
+ target_compile_options(nw_${example} PRIVATE ${sup_cflags})
+endfunction()
+
+nw_example(minimal)
+nw_example(proc_exit)
+nw_example(file)
diff --git a/examples/example.wasm b/examples/example.wasm
new file mode 100644
index 0000000..25871e0
--- /dev/null
+++ b/examples/example.wasm
Binary files differ
diff --git a/examples/file.c b/examples/file.c
new file mode 100644
index 0000000..c913830
--- /dev/null
+++ b/examples/file.c
@@ -0,0 +1,659 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct inst
+{
+ long retval;
+ size_t n_files;
+
+ struct file
+ {
+ FILE *f;
+ int fd;
+ } *files;
+
+ struct mem
+ {
+ void *p;
+ size_t n;
+ } stack, linear, global;
+};
+
+static int io_read(void *const buf, const size_t n, void *const user)
+{
+ FILE *const f = user;
+ const size_t r = fread(buf, 1, 1, f);
+
+ if (!r && ferror(f))
+ {
+ fprintf(stderr, "ferror\n");
+ return -1;
+ }
+
+ return r;
+}
+
+static enum nw_state io_seek(const long offset, void *const user)
+{
+ if (fseek(user, offset, SEEK_SET))
+ {
+ fprintf(stderr, "fseek(3): %s\n", strerror(errno));
+ return NW_FATAL;
+ }
+
+ return NW_OK;
+}
+
+static enum nw_state io_tell(long *const out, void *const user)
+{
+ const long offset = ftell(user);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "ftell(3): %s\n", strerror(errno));
+ return NW_FATAL;
+ }
+
+ *out = offset;
+ return NW_OK;
+}
+
+static int io_eof(void *const user)
+{
+ FILE *const f = user;
+
+ return feof(f);
+}
+
+static int push(const void *const src, const size_t n, void *const user)
+{
+ struct inst *const inst = user;
+ struct mem *const s = &inst->stack;
+ char *const p = realloc(s->p, s->n + n);
+
+ if (!p)
+ {
+ fprintf(stderr, "realloc(3): %s\n", strerror(errno));
+ return -1;
+ }
+
+ fprintf(stderr, "pushed %zu bytes: {", n);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ memcpy(&p[s->n], src, n);
+ s->p = p;
+ s->n += n;
+ fprintf(stderr, "}, stack size=%zu\n", s->n);
+ return n;
+}
+
+static int pop(void *const dst, const size_t n, void *const user)
+{
+ struct inst *const inst = user;
+ struct mem *const s = &inst->stack;
+
+ if (s->n < n)
+ {
+ fprintf(stderr, "stack underflow\n");
+ return -1;
+ }
+
+ const char *const src = (const char *)s->p + (s->n - n);
+
+ fprintf(stderr, "popped %zu bytes: {", n);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ if (dst)
+ memcpy(dst, src, n);
+
+ if (s->n == n)
+ {
+ free(s->p);
+ s->p = NULL;
+ }
+ else
+ {
+ char *const p = realloc(s->p, s->n - n);
+
+ if (!p)
+ {
+ fprintf(stderr, "realloc(3): %s\n", strerror(errno));
+ return -1;
+ }
+
+ s->p = p;
+ }
+
+ s->n -= n;
+ fprintf(stderr, "}, stack size=%zu\n", s->n);
+ return n;
+}
+
+static size_t ptr(void *const user)
+{
+ return ((const struct inst *)user)->stack.n;
+}
+
+static int load(const nw_varuint32 offset, void *const dst, const size_t n,
+ const struct mem *const m)
+{
+ if (n > m->n || offset > m->n - n)
+ {
+ fprintf(stderr, "out-of-bounds access, offset=%lu, n=%zu\n",
+ (unsigned long)offset, (unsigned long)n);
+ return -1;
+ }
+
+ fprintf(stderr, "loaded %zu bytes from offset %lu: {", (unsigned long)n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy(dst, (const char *)m->p + offset, n);
+ return n;
+}
+
+static int store(const nw_varuint32 offset, const void *const src,
+ const size_t n, const struct mem *const m)
+{
+ if (n > m->n || offset > m->n - n)
+ {
+ fprintf(stderr, "out-of-bounds access, offset=%lu, n=%lu\n",
+ (unsigned long)offset, (unsigned long)n);
+ return -1;
+ }
+
+ fprintf(stderr, "stored %lu bytes into offset %lu: {", (unsigned long)n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy((char *)m->p + offset, src, n);
+ return n;
+}
+
+static int stread(const size_t offset, void *const dst, const size_t n,
+ void *const user)
+{
+ const struct inst *const inst = user;
+
+ return load(offset, dst, n, &inst->stack);
+}
+
+static int stwrite(const size_t offset, const void *const src, const size_t n,
+ void *const user)
+{
+ const struct inst *const inst = user;
+
+ return store(offset, src, n, &inst->stack);
+}
+
+static int ensure_linear(struct mem *const m, const unsigned long offset,
+ const size_t n)
+{
+ void *const p = realloc(m->p, offset + n);
+
+ if (!p)
+ {
+ fprintf(stderr, "realloc(3): %s\n", strerror(errno));
+ return -1;
+ }
+
+ m->p = p;
+ m->n = offset + n;
+ memset((char *)m->p + offset, 0, n);
+ return 0;
+}
+
+static int lnload(const unsigned long offset, void *const dst, const size_t n,
+ void *const user)
+{
+ struct inst *const i = user;
+ struct mem *const m = &i->linear;
+
+ if (offset >= m->n && ensure_linear(m, offset, n))
+ {
+ fprintf(stderr, "ensure_linear failed\n");
+ return -1;
+ }
+
+ fprintf(stderr, "loaded %lu bytes from offset %lu: {", (unsigned long)n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy(dst, (const char *)m->p + offset, n);
+ return n;
+}
+
+static int lnstore(const unsigned long offset, const void *const src,
+ const size_t n, void *const user)
+{
+ struct inst *const i = user;
+ struct mem *const m = &i->linear;
+
+ if (offset >= m->n && ensure_linear(m, offset, n))
+ {
+ fprintf(stderr, "ensure_linear failed\n");
+ return -1;
+ }
+
+ fprintf(stderr, "stored %lu bytes into offset %lu: {", (unsigned long)n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy((char *)m->p + offset, src, n);
+ return n;
+}
+
+static int glload(const unsigned long offset, void *const dst,
+ const size_t n, void *const user)
+{
+ const struct inst *const i = user;
+
+ return load(offset, dst, n, &i->global);
+}
+
+static int glstore(const unsigned long offset, const void *const src,
+ const size_t n, void *const user)
+{
+ const struct inst *const i = user;
+
+ return store(offset, src, n, &i->global);
+}
+
+static enum nw_state wasi_exit(const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ struct inst *const i = user;
+
+ i->retval = params->i32;
+ return NW_OK;
+}
+
+static enum nw_state wasi_fd_close(const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ return NW_FATAL;
+}
+
+static enum nw_state wasi_fd_fdstat_get(const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ return NW_FATAL;
+}
+
+static enum nw_state wasi_fd_prestat_get(const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ /*
+ * From wasi/phases/old/snapshot_0/docs.md:
+ *
+ * fd_prestat_get(fd: fd) -> (errno, prestat)
+ * Return a description of the given preopened file descriptor.
+ * Params
+ * fd: fd
+ * Results
+ * error: errno
+ * buf: prestat The buffer where the description is stored.
+ *
+ * Offset: 0
+ * prestat: Union
+ * Information about a pre-opened capability.
+ * Size: 8
+ * Alignment: 4
+ * Union Layout
+ * tag_size: 1
+ * tag_align: 1
+ * contents_offset: 4
+ * contents_size: 4
+ * contents_align: 4
+ */
+
+ const long fd = params[0].i32;
+ const unsigned long prestat = params[1].i32;
+ const struct file *fl = NULL;
+ struct inst *const inst = user;
+
+ fprintf(stderr, "fd=%lu, prestat=%#lx\n", fd, prestat);
+
+ for (size_t i = 0; i < inst->n_files; i++)
+ {
+ const struct file *const f = &inst->files[i];
+
+ if (fd == (unsigned long)f->fd)
+ {
+ fl = f;
+ break;
+ }
+ }
+
+ if (!fl)
+ {
+ /* __WASI_EBADF */
+ ret->i32 = 8;
+ return NW_OK;
+ }
+
+ return NW_OK;
+}
+
+static enum nw_state wasi_fd_prestat_dir_name(
+ const union nw_value *const params, union nw_value *const ret, void *user)
+{
+ return NW_FATAL;
+}
+
+static enum nw_state wasi_fd_read( const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ const long fd = params[0].i32;
+ const unsigned long iovec = params[1].i32, n = params[2].i32,
+ nread = params[3].i32;
+
+ fprintf(stderr, "fd=%ld, iovec=%#lx, n=%lu, nread=%#lx\n",
+ fd, iovec, n, nread);
+ return NW_FATAL;
+}
+
+static enum nw_state wasi_path_open( const union nw_value *const params,
+ union nw_value *const ret, void *user)
+{
+ return NW_FATAL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+ FILE *f = NULL;
+ struct inst ins = {0};
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "%s <wasm>\n", *argv);
+ goto end;
+ }
+
+ const char *const path = argv[1];
+
+ if (!(f = fopen(path, "rb")))
+ {
+ fprintf(stderr, "fopen(3) %s: %s\n", path, strerror(errno));
+ goto end;
+ }
+
+ static const struct nw_import imports[] =
+ {
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "proc_exit",
+ .u.function =
+ {
+ .fn = wasi_exit,
+ .signature = "(i)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "fd_close",
+ .u.function =
+ {
+ .fn = wasi_fd_close,
+ .signature = "i(i)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "fd_fdstat_get",
+ .u.function =
+ {
+ .fn = wasi_fd_fdstat_get,
+ .signature = "i(ii)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "fd_prestat_get",
+ .u.function =
+ {
+ .fn = wasi_fd_prestat_get,
+ .signature = "i(ii)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "fd_prestat_dir_name",
+ .u.function =
+ {
+ .fn = wasi_fd_prestat_dir_name,
+ .signature = "i(ii)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "fd_read",
+ .u.function =
+ {
+ .fn = wasi_fd_read,
+ .signature = "i(iiii)"
+ }
+ },
+
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "path_open",
+ .u.function =
+ {
+ .fn = wasi_path_open,
+ .signature = "i(iiiiiIIii)"
+ }
+ },
+ };
+
+ const struct nw_io_cfg io =
+ {
+ .read = io_read,
+ .seek = io_seek,
+ .tell = io_tell,
+ .eof = io_eof,
+ .user = f
+ };
+
+ enum {N_IMPORTS = sizeof imports / sizeof *imports};
+
+ const struct nw_mod_cfg cfg =
+ {
+ .imports = imports,
+ .imp_indexes = (struct nw_import_index[N_IMPORTS]){{0}},
+ .n_imports = N_IMPORTS,
+ .io = io
+ };
+
+ struct nw_mod m;
+ struct nw_mod_out mout;
+
+ nw_init(&m, &cfg);
+
+again:
+
+ switch (nw_load(&m, &mout))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+ goto again;
+
+ case NW_FATAL:
+ fprintf(stderr, "nw_load failed\n");
+ goto end;
+ }
+
+ if (!(ins.global.p = malloc(ins.global.n = mout.global)))
+ {
+ fprintf(stderr, "malloc(3): %s\n", strerror(errno));
+ goto end;
+ }
+
+ enum {ARGS = 10};
+ const struct nw_inst_cfg icfg =
+ {
+ .entry = "_start",
+ .interp_cfg =
+ {
+ .io = io,
+ .m = &m,
+ .user = &ins,
+ .args = (union nw_value[ARGS]){{0}},
+ .n_args = ARGS,
+ .stack =
+ {
+ .push = push,
+ .pop = pop,
+ .ptr = ptr,
+ .read = stread,
+ .write = stwrite
+ },
+
+ .linear =
+ {
+ .load = lnload,
+ .store = lnstore
+ },
+
+ .global =
+ {
+ .load = glload,
+ .store = glstore
+ }
+ }
+ };
+
+ struct nw_inst inst;
+
+ if (nw_start(&inst, &icfg))
+ {
+ fprintf(stderr, "nw_start failed\n");
+ goto end;
+ }
+
+again2:
+
+ switch (nw_run(&inst))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+
+ if (ins.retval)
+ {
+ ret = ins.retval;
+ fprintf(stderr, "instance exited with status %d\n", ret);
+ goto end;
+ }
+
+ goto again2;
+
+ case NW_FATAL:
+ fprintf(stderr, "nw_run failed: %s\n", nw_rexc(&inst));
+ goto end;
+ }
+
+ ret = EXIT_SUCCESS;
+
+end:
+
+ for (size_t i = 0; i < ins.n_files; i++)
+ {
+ const struct file *const f = &ins.files[i];
+
+ if (fclose(f->f))
+ {
+ fprintf(stderr, "fclose(3) [%lu]: %s\n", (unsigned long)i,
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ }
+ }
+
+ if (f && fclose(f))
+ {
+ fprintf(stderr, "fclose(3) %s: %s\n", path, strerror(errno));
+ goto end;
+ }
+
+ free(ins.files);
+ free(ins.stack.p);
+ free(ins.linear.p);
+ free(ins.global.p);
+ return ret;
+}
diff --git a/examples/minimal.c b/examples/minimal.c
new file mode 100644
index 0000000..8812ac5
--- /dev/null
+++ b/examples/minimal.c
@@ -0,0 +1,136 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+static int io_read(void *const buf, const size_t n, void *const user)
+{
+ return -1;
+}
+
+static enum nw_state io_seek(const long offset, void *const user)
+{
+ return -1;
+}
+
+static enum nw_state io_tell(long *const out, void *const user)
+{
+ return -1;
+}
+
+static int io_eof(void *const user)
+{
+ return -1;
+}
+
+static int push(const void *const src, const size_t n, void *const user)
+{
+ return -1;
+}
+
+static int pop(void *const dst, const size_t n, void *const user)
+{
+ return -1;
+}
+
+static size_t ptr(void *const user)
+{
+ return 0;
+}
+
+static int load(const nw_varuint32 offset, void *const dst, const size_t n,
+ void *const user)
+{
+ return -1;
+}
+
+static int store(const nw_varuint32 offset, const void *const src,
+ const size_t n, void *const user)
+{
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ const struct nw_io_cfg io =
+ {
+ .read = io_read,
+ .seek = io_seek,
+ .tell = io_tell,
+ .eof = io_eof
+ };
+
+ const struct nw_mod_cfg cfg =
+ {
+ .io = io
+ };
+
+ struct nw_mod m;
+ struct nw_mod_out mout;
+
+ nw_init(&m, &cfg);
+
+again:
+
+ switch (nw_load(&m, &mout))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+ goto again;
+
+ case NW_FATAL:
+ return EXIT_FAILURE;
+ }
+
+ const struct nw_inst_cfg icfg =
+ {
+ .interp_cfg =
+ {
+ .io = io,
+ .m = &m,
+ .stack =
+ {
+ .push = push,
+ .pop = pop,
+ .ptr = ptr
+ },
+
+ .linear =
+ {
+ .load = load,
+ .store = store
+ }
+ }
+ };
+
+ struct nw_inst inst;
+
+ if (nw_start(&inst, &icfg))
+ return EXIT_FAILURE;
+
+again2:
+
+ switch (nw_run(&inst))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+ goto again2;
+
+ case NW_FATAL:
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/examples/proc_exit.c b/examples/proc_exit.c
new file mode 100644
index 0000000..d4a580e
--- /dev/null
+++ b/examples/proc_exit.c
@@ -0,0 +1,484 @@
+/*
+ * nanowasm, a tiny WebAssembly/Wasm interpreter
+ * Copyright (C) 2023-2025 Xavier Del Campo Romero
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include <nanowasm/nw.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct inst
+{
+ long retval;
+
+ struct mem
+ {
+ void *p;
+ size_t n;
+ } stack, linear, global;
+};
+
+static int io_read(void *const buf, const size_t n, void *const user)
+{
+ FILE *const f = user;
+ const size_t r = fread(buf, 1, 1, f);
+
+ if (!r && ferror(f))
+ {
+ fprintf(stderr, "%s: ferror\n", __func__);
+ return -1;
+ }
+
+ return r;
+}
+
+static enum nw_state io_seek(const long offset, void *const user)
+{
+ if (fseek(user, offset, SEEK_SET))
+ {
+ fprintf(stderr, "%s: fseek(3): %s\n", __func__, strerror(errno));
+ return NW_FATAL;
+ }
+
+ return NW_OK;
+}
+
+static enum nw_state io_tell(long *const out, void *const user)
+{
+ const long offset = ftell(user);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return NW_FATAL;
+ }
+
+ *out = offset;
+ return NW_OK;
+}
+
+static int io_eof(void *const user)
+{
+ FILE *const f = user;
+
+ return feof(f);
+}
+
+static int push(const void *const src, const size_t n, void *const user)
+{
+ struct inst *const inst = user;
+ struct mem *const s = &inst->stack;
+ char *const p = realloc(s->p, s->n + n);
+
+ if (!p)
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ fprintf(stderr, "pushed %zu bytes: {", n);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ memcpy(&p[s->n], src, n);
+ s->p = p;
+ s->n += n;
+ fprintf(stderr, "}, stack size=%zu\n", s->n);
+ return n;
+}
+
+static int pop(void *const dst, const size_t n, void *const user)
+{
+ struct inst *const inst = user;
+ struct mem *const s = &inst->stack;
+
+ if (s->n < n)
+ {
+ fprintf(stderr, "%s: stack underflow\n", __func__);
+ return -1;
+ }
+
+ const char *const src = (const char *)s->p + (s->n - n);
+
+ fprintf(stderr, "popped %zu bytes: {", n);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ memcpy(dst, src, n);
+
+ if (s->n == n)
+ {
+ free(s->p);
+ s->p = NULL;
+ }
+ else
+ {
+ char *const p = realloc(s->p, s->n - n);
+
+ if (!p)
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ s->p = p;
+ }
+
+ s->n -= n;
+ fprintf(stderr, "}, stack size=%zu\n", s->n);
+ return n;
+}
+
+static size_t ptr(void *const user)
+{
+ return ((const struct inst *)user)->stack.n;
+}
+
+static int load(const nw_varuint32 offset, void *const dst, const size_t n,
+ const struct mem *const m)
+{
+ if (n > m->n || offset > m->n - n)
+ {
+ fprintf(stderr, "%s: out-of-bounds access, offset=%lu, n=%zu\n",
+ __func__, (unsigned long)offset, n);
+ return -1;
+ }
+
+ fprintf(stderr, "%s: loaded %zu bytes from offset %lu: {", __func__, n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy(dst, (const char *)m->p + offset, n);
+ return n;
+}
+
+static int store(const nw_varuint32 offset, const void *const src,
+ const size_t n, const struct mem *const m)
+{
+ if (n > m->n || offset > m->n - n)
+ {
+ fprintf(stderr, "%s: out-of-bounds access, offset=%lu, n=%zu\n",
+ __func__, (unsigned long)offset, n);
+ return -1;
+ }
+
+ fprintf(stderr, "%s: stored %zu bytes into offset %lu: {", __func__, n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy((char *)m->p + offset, src, n);
+ return n;
+}
+
+static int stread(const size_t offset, void *const dst, const size_t n,
+ void *const user)
+{
+ const struct inst *const inst = user;
+
+ return load(offset, dst, n, &inst->stack);
+}
+
+static int stwrite(const size_t offset, const void *const src, const size_t n,
+ void *const user)
+{
+ const struct inst *const inst = user;
+
+ return store(offset, src, n, &inst->stack);
+}
+
+static int ensure_linear(struct mem *const m, const unsigned long offset,
+ const size_t n)
+{
+ void *const p = realloc(m->p, offset + n);
+
+ if (!p)
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ m->p = p;
+ m->n = offset + n;
+ memset((char *)m->p + offset, 0, n);
+ return 0;
+}
+
+static int lnload(const unsigned long offset, void *const dst, const size_t n,
+ void *const user)
+{
+ struct inst *const i = user;
+ struct mem *const m = &i->linear;
+
+ if (offset >= m->n && ensure_linear(m, offset, n))
+ {
+ fprintf(stderr, "%s: ensure_linear failed\n", __func__);
+ return -1;
+ }
+
+ fprintf(stderr, "%s: loaded %zu bytes from offset %lu: {", __func__, n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)m->p)[offset + i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy(dst, (const char *)m->p + offset, n);
+ return n;
+}
+
+static int lnstore(const unsigned long offset, const void *const src,
+ const size_t n, void *const user)
+{
+ struct inst *const i = user;
+ struct mem *const m = &i->linear;
+
+ if (offset >= m->n && ensure_linear(m, offset, n))
+ {
+ fprintf(stderr, "%s: ensure_linear failed\n", __func__);
+ return -1;
+ }
+
+ fprintf(stderr, "%s: stored %zu bytes into offset %lu: {", __func__, n,
+ (unsigned long)offset);
+
+ for (size_t i = 0; i < n; i++)
+ {
+ fprintf(stderr, "%hhu", ((const char *)src)[i]);
+
+ if (i + 1 < n)
+ fputs(", ", stderr);
+ }
+
+ fputs("}\n", stderr);
+ memcpy((char *)m->p + offset, src, n);
+ return n;
+}
+
+static int glload(const unsigned long offset, void *const dst,
+ const size_t n, void *const user)
+{
+ const struct inst *const i = user;
+
+ return load(offset, dst, n, &i->global);
+}
+
+static int glstore(const unsigned long offset, const void *const src,
+ const size_t n, void *const user)
+{
+ const struct inst *const i = user;
+
+ return store(offset, src, n, &i->global);
+}
+
+static enum nw_state wasi_exit(const union nw_value *const params,
+ union nw_value *const ret, void *user, struct nw_next *const next)
+{
+ struct inst *const i = user;
+
+ i->retval = params->i32;
+ return NW_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = EXIT_FAILURE;
+ FILE *f = NULL;
+ struct inst ins = {0};
+
+ if (argc != 2)
+ {
+ fprintf(stderr, "%s <wasm>\n", *argv);
+ goto end;
+ }
+
+ const char *const path = argv[1];
+
+ if (!(f = fopen(path, "rb")))
+ {
+ fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, path,
+ strerror(errno));
+ goto end;
+ }
+
+ static const struct nw_import imports[] =
+ {
+ {
+ .kind = NW_KIND_FUNCTION,
+ .module = "wasi_snapshot_preview1",
+ .field = "proc_exit",
+ .u.function =
+ {
+ .fn = wasi_exit,
+ .signature = "(i)"
+ }
+ }
+ };
+
+ const struct nw_io_cfg io =
+ {
+ .read = io_read,
+ .seek = io_seek,
+ .tell = io_tell,
+ .eof = io_eof,
+ .user = f
+ };
+
+ enum {N_IMPORTS = sizeof imports / sizeof *imports};
+
+ const struct nw_mod_cfg cfg =
+ {
+ .imports = imports,
+ .imp_indexes = (struct nw_import_index[N_IMPORTS]){{0}},
+ .n_imports = N_IMPORTS,
+ .io = io
+ };
+
+ struct nw_mod m;
+ struct nw_mod_out mout;
+
+ nw_init(&m, &cfg);
+
+again:
+
+ switch (nw_load(&m, &mout))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+ goto again;
+
+ case NW_FATAL:
+ fprintf(stderr, "%s: nw_load failed\n", __func__);
+ goto end;
+ }
+
+ if (!(ins.global.p = malloc(ins.global.n = mout.global)))
+ {
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ goto end;
+ }
+
+ enum {ARGS = 1};
+ const struct nw_inst_cfg icfg =
+ {
+ .entry = "_start",
+ .interp_cfg =
+ {
+ .io = io,
+ .m = &m,
+ .user = &ins,
+ .args = (union nw_value[ARGS]){{0}},
+ .n_args = ARGS,
+ .stack =
+ {
+ .push = push,
+ .pop = pop,
+ .ptr = ptr,
+ .read = stread,
+ .write = stwrite
+ },
+
+ .linear =
+ {
+ .load = lnload,
+ .store = lnstore
+ },
+
+ .global =
+ {
+ .load = glload,
+ .store = glstore
+ }
+ }
+ };
+
+ struct nw_inst inst;
+
+ if (nw_start(&inst, &icfg))
+ {
+ fprintf(stderr, "%s: nw_start failed\n", __func__);
+ goto end;
+ }
+
+again2:
+
+ switch (nw_run(&inst))
+ {
+ case NW_OK:
+ break;
+
+ case NW_AGAIN:
+
+ if (ins.retval)
+ {
+ ret = ins.retval;
+ fprintf(stderr, "instance exited with status %d\n", ret);
+ goto end;
+ }
+
+ goto again2;
+
+ case NW_FATAL:
+ fprintf(stderr, "%s: nw_run failed: %s\n", __func__,
+ nw_rexc(&inst));
+ goto end;
+ }
+
+ ret = EXIT_SUCCESS;
+
+end:
+
+ if (f && fclose(f))
+ {
+ fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, path,
+ strerror(errno));
+ goto end;
+ }
+
+ free(ins.stack.p);
+ free(ins.linear.p);
+ free(ins.global.p);
+ return ret;
+}