aboutsummaryrefslogtreecommitdiff
path: root/examples/proc_exit.c
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/proc_exit.c
downloadnanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz
First commit
Diffstat (limited to 'examples/proc_exit.c')
-rw-r--r--examples/proc_exit.c484
1 files changed, 484 insertions, 0 deletions
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;
+}