diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-09-07 00:04:38 +0200 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi92@disroot.org> | 2025-11-06 14:38:40 +0100 |
| commit | 6d9d80362f9932bbc87e162b8ef7df06c73e27e1 (patch) | |
| tree | e3e228c63fe26f07503f226de7fb5086b3dc2286 /examples/proc_exit.c | |
| download | nanowasm-6d9d80362f9932bbc87e162b8ef7df06c73e27e1.tar.gz | |
First commit
Diffstat (limited to 'examples/proc_exit.c')
| -rw-r--r-- | examples/proc_exit.c | 484 |
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; +} |
