/* * nanowasm, a tiny WebAssembly/Wasm interpreter * Copyright (C) 2023-2024 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 #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 void (*const checks[])(struct nw_mod *) = { [OP_UNREACHABLE] = nwp_op_check_no_immediate, [OP_NOP] = nwp_op_check_no_immediate, [OP_END] = nwp_op_check_end, [OP_BLOCK] = nwp_op_check_block, [OP_BR_IF] = nwp_op_check_br_if, [OP_RETURN] = nwp_op_check_no_immediate, [OP_CALL] = nwp_op_check_call, [OP_GET_LOCAL] = nwp_op_check_local_index, [OP_SET_LOCAL] = nwp_op_check_local_index, [OP_TEE_LOCAL] = nwp_op_check_local_index, [OP_GET_GLOBAL] = nwp_op_check_global_index, [OP_SET_GLOBAL] = nwp_op_check_global_index, [OP_I32_LOAD] = nwp_op_check_memory_immediate, [OP_I32_STORE] = nwp_op_check_memory_immediate, [OP_I64_STORE] = nwp_op_check_memory_immediate, [OP_F32_STORE] = nwp_op_check_memory_immediate, [OP_F64_STORE] = nwp_op_check_memory_immediate, [OP_I32_CONST] = nwp_op_check_i32_const, [OP_I64_CONST] = nwp_op_check_i64_const, [OP_I32_SUB] = nwp_op_check_no_immediate }; static enum nw_state get_post_offset(struct nw_mod *const m) { long offset; 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; const unsigned long consumed = offset - c->op_off; if (consumed && consumed >= c->rem) { LOG("%s: parameters exceed expected body length\n", __func__); return NW_FATAL; } c->rem -= consumed; m->next = get_opcode; return NW_AGAIN; } 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); void (*const fn)(struct nw_mod *) = checks[c->op]; if (n) return n; else if (!fn) { LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, c->op); return NW_FATAL; } 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) { LOG("%s: mismatched number of blocks in function %lu\n", __func__, (unsigned long)c->entry_i); 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; if (!c->rem) return check_exit(m); const struct nw_io_cfg *const cfg = &m->cfg.io; struct nw_sm_io io = {.buf = &c->op, .n = sizeof c->op}; const enum nw_state n = nwp_io_read(cfg, &io); if (n) return n; else if (c->rem == 1 && c->op != OP_END) { LOG("%s: unexpected opcode %#" PRIx8 " at body end\n", __func__, c->op); return NW_FATAL; } else if (c->op >= sizeof checks / sizeof *checks) { LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, c->op); return NW_FATAL; } c->rem--; m->next = get_op_offset; return NW_AGAIN; } static enum nw_state get_rem(struct nw_mod *const m) { long offset; const struct nw_io_cfg *const cfg = &m->cfg.io; const enum nw_state n = cfg->tell(&offset, cfg->user); if (n) return n; struct nw_sm_c *const c = &m->sm.code; const unsigned long consumed = offset - c->start; if (consumed >= c->body_size) { LOG("%s: exceeded function body size\n", __func__); return NW_FATAL; } c->rem = c->body_size - consumed; m->next = get_opcode; 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); 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); 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); 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); if (n) return n; 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; c->fn = (const struct nw_sm_c_fn){.blocks = 1}; 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); if (n) return n; m->next = entry_loop; return NW_AGAIN; } void nwp_section_code(struct nw_mod *const m) { struct nw_sm_c *const c = &m->sm.code; m->next = get_count; *c = (const struct nw_sm_c){0}; }