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 | |
First commit
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | examples/example.wasm | bin | 0 -> 26749 bytes | |||
| -rw-r--r-- | examples/file.c | 659 | ||||
| -rw-r--r-- | examples/minimal.c | 136 | ||||
| -rw-r--r-- | examples/proc_exit.c | 484 |
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 Binary files differnew file mode 100644 index 0000000..25871e0 --- /dev/null +++ b/examples/example.wasm 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; +} |
