/* * 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 static enum nw_state local_loop(struct nw_mod *); static enum nw_state get_opcode(struct nw_mod *); static enum nw_state entry_loop(struct nw_mod *); static enum nw_state get_post_offset(struct nw_mod *const m) { long offset; unsigned long consumed; struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->tell(&offset, cfg->user); if (n) return n; else if ((consumed = offset - c->op_off) && consumed >= c->rem) { #ifdef NW_LOG nwp_log("parameters exceed expected body length\n"); #endif return NW_FATAL; } c->rem -= consumed; m->next = get_opcode; return NW_AGAIN; } typedef void (*op_fn)(struct nw_mod *); static op_fn find(unsigned char op) { size_t i; for (i = 0; i < nwp_check_ops.n; i++) { const struct nwp_check_op *const o = &nwp_check_ops.ops[i]; if (op >= o->start && op <= o->end) return o->f; } return NULL; } static enum nw_state get_op_offset(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->tell(&c->op_off, cfg->user); op_fn fn; if (n) return n; else if (!(fn = find(c->op))) { #ifdef NW_LOG nwp_log("invalid opcode %#hhx\n", (unsigned char)c->op); #endif return NW_FATAL; } #ifdef NW_LOG nwp_log("opcode: %#hhx\n", (unsigned char)c->op); #endif c->next = get_post_offset; fn(m); return NW_AGAIN; } static enum nw_state check_exit(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; struct nw_sm_c_fn *const fn = &c->fn; if (fn->blocks) { #ifdef NW_LOG nwp_log("mismatched number of blocks in function %lu\n", (unsigned long)c->entry_i); #endif return NW_FATAL; } c->entry_i++; m->next = entry_loop; return NW_AGAIN; } static enum nw_state get_opcode(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_io io = {0}; enum nw_state n; io.buf = &c->op; io.n = sizeof c->op; if (!c->rem) return check_exit(m); else if ((n = nwp_io_read(cfg, &io, cfg->user))) return n; else if (c->rem == 1 && c->op != OP_END) { #ifdef NW_LOG nwp_log("%s: unexpected opcode %#x at body end\n", (unsigned)c->op); #endif return NW_FATAL; } c->rem--; m->next = get_op_offset; return NW_AGAIN; } #ifndef NW_CHECK_CODE static enum nw_state skip(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->seek(c->body_start + c->rem, cfg->user); if (n) return n; c->entry_i++; m->next = entry_loop; return NW_AGAIN; } #endif static enum nw_state get_rem(struct nw_mod *const m) { unsigned long consumed; struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->tell(&c->body_start, cfg->user); if (n) return n; else if ((consumed = c->body_start - c->start) >= c->body_size) { #ifdef NW_LOG nwp_log("exceeded function body size\n"); #endif return NW_FATAL; } c->rem = c->body_size - consumed; #ifdef NW_CHECK_CODE m->next = get_opcode; #else m->next = skip; #endif return NW_AGAIN; } static enum nw_state get_type(struct nw_mod *const m) { nw_varint7 type; const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_c *const c = &m->sm.code; struct nw_sm_leb128 *const l = &c->leb128; const enum nw_state n = nwp_varint7(cfg, l, &type, cfg->user); if (n) return n; c->fn.local_i++; m->next = local_loop; return NW_AGAIN; } static enum nw_state get_group_count(struct nw_mod *const m) { nw_varuint32 group_count; const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_c *const c = &m->sm.code; struct nw_sm_leb128 *const l = &c->leb128; const enum nw_state n = nwp_varuint32(cfg, l, &group_count, cfg->user); if (n) return n; c->fn.local_total += group_count; m->next = get_type; return NW_AGAIN; } static enum nw_state local_loop(struct nw_mod *const m) { const struct nw_sm_c_fn *const fn = &m->sm.code.fn; m->next = fn->local_i < fn->local_count ? get_group_count : get_rem; return NW_AGAIN; } static enum nw_state get_local_count(struct nw_mod *const m) { const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_c *const c = &m->sm.code; struct nw_sm_c_fn *const fn = &c->fn; struct nw_sm_leb128 *const l = &c->leb128; const enum nw_state n = nwp_varuint32(cfg, l, &fn->local_count, cfg->user); if (n) return n; m->next = fn->local_count ? local_loop : get_rem; return NW_AGAIN; } static enum nw_state get_start(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->tell(&c->start, cfg->user); if (n) return n; m->next = get_local_count; return NW_AGAIN; } static enum nw_state get_body_size(struct nw_mod *const m) { const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_c *const c = &m->sm.code; struct nw_sm_leb128 *const l = &c->leb128; const enum nw_state n = nwp_varuint32(cfg, l, &c->body_size, cfg->user); if (n) return n; else if (!c->body_size) { #ifdef NW_LOG nwp_log("unexpected zero size for function body %lu\n", (unsigned long)c->entry_i); #endif return NW_FATAL; } m->next = get_start; return NW_AGAIN; } static enum nw_state entry_loop(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; struct nw_sm_c_fn fn = {0}; fn.blocks = 1; c->fn = fn; m->next = c->entry_i < c->count ? get_body_size : nwp_section_exit; return NW_AGAIN; } static enum nw_state get_count(struct nw_mod *const m) { const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_c *const c = &m->sm.code; struct nw_sm_leb128 *const l = &c->leb128; const enum nw_state n = nwp_varuint32(cfg, l, &c->count, cfg->user); if (n) return n; m->next = entry_loop; return NW_AGAIN; } void nwp_section_code(struct nw_mod *const m) { const struct nw_sm_c c = {0}; m->sm.code = c; m->next = get_count; }