/* * 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 #include #include #include #include #include #include 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 \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; }