diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-11-26 22:43:30 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2024-04-21 01:51:24 +0200 |
| commit | f25b015e5b668028c34974bbb22faa4105c26690 (patch) | |
| tree | 28f2b08c17b3585d06694ad74004d0617eadb785 /src | |
| download | nanowasm-sync-f25b015e5b668028c34974bbb22faa4105c26690.tar.gz | |
First commit
Diffstat (limited to 'src')
58 files changed, 4818 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..77be1e5 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + fstrcmp.c + interp.c + leb128.c + load.c + run.c + search.c + start.c + stop.c + types.c +) +add_subdirectory(op) +add_subdirectory(section) diff --git a/src/fstrcmp.c b/src/fstrcmp.c new file mode 100644 index 0000000..b26baa8 --- /dev/null +++ b/src/fstrcmp.c @@ -0,0 +1,41 @@ +/* + * 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 <nw/fstring.h> +#include <nw/log.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +int fstrcmp(const char *str, FILE *const f, const bool abort) +{ + int ret = 0; + + for (size_t i = 0; i < strlen(str); i++) + { + uint8_t byte; + + if (!fread(&byte, sizeof byte, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + else if (byte != str[i]) + { + if (abort) + return -1; + else + ret = -1; + } + } + + return ret; +} diff --git a/src/interp.c b/src/interp.c new file mode 100644 index 0000000..3a32df0 --- /dev/null +++ b/src/interp.c @@ -0,0 +1,516 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/log.h> +#include <nw/interp.h> +#include <nw/opcodes.h> +#include <nw/ops.h> +#include <nw/sections.h> +#include <nw/types.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static const enum opcode initexpr_opcodes[] = +{ + OP_NOP, + OP_END, + OP_I32_CONST, + OP_I64_CONST, + OP_F32_CONST, + OP_F64_CONST +}; + +const struct interp_set interp_initexpr_set = +{ + .opcodes = initexpr_opcodes, + .n = sizeof initexpr_opcodes / sizeof *initexpr_opcodes +}; + +static int (*const ops[])(struct nw_interp *) = +{ + [OP_UNREACHABLE] = op_unreachable, + [OP_NOP] = op_nop, + [OP_BLOCK] = op_block, + [OP_LOOP] = op_loop, + [OP_IF] = op_if, + [OP_ELSE] = op_else, + [OP_END] = op_end, + [OP_BR] = op_br, + [OP_BR_IF] = op_br_if, + [OP_BR_TABLE] = op_br_table, + [OP_RETURN] = op_return, + [OP_CALL] = op_call, + [OP_CALL_INDIRECT] = op_call_indirect, + [OP_GET_LOCAL] = op_get_local, + [OP_SET_LOCAL] = op_set_local, + [OP_TEE_LOCAL] = op_tee_local, + [OP_GET_GLOBAL] = op_get_global, + [OP_SET_GLOBAL] = op_set_global, + [OP_I32_LOAD] = op_i32_load, + [OP_I32_STORE] = op_i32_store, + [OP_I32_CONST] = op_i32_const, + [OP_I64_CONST] = op_i64_const, + [OP_F32_CONST] = op_f32_const, + [OP_F64_CONST] = op_f64_const, + [OP_I32_SUB] = op_i32_sub +}; + +int interp_run(struct nw_interp *const i) +{ + uint8_t op; + + if (!fread(&op, sizeof op, 1, i->f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(i->f), + ferror(i->f)); + i->exception = "I/O error"; + return -1; + } + else if (op >= sizeof ops / sizeof *ops) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op); + i->exception = "invalid opcode"; + return -1; + } + else if (!ops[op]) + { + LOG("%s: unsupported opcode %#" PRIx8 "\n", __func__, op); + i->exception = "invalid opcode"; + return -1; + } + + return ops[op](i); +} + +static int run_opcode_limited(struct nw_interp *const in, + const struct interp_set *const set) +{ + uint8_t op; + + if (!fread(&op, sizeof op, 1, in->f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(in->f), ferror(in->f)); + in->exception = "I/O error"; + return -1; + } + + for (size_t i = 0; i < set->n; i++) + if (op == set->opcodes[i]) + return ops[op](in); + + LOG("%s: unexpected opcode %#" PRIx8 "\n", __func__, op); + in->exception = "invalid opcode"; + return -1; +} + +int interp_run_limited(struct nw_interp *const i, + const struct interp_set *const set) +{ + while (!i->exit) + if (run_opcode_limited(i, set)) + { + LOG("%s: run_opcode_limited failed\n", __func__); + return -1; + } + + return 0; +} + +int interp_check_opcode(const uint8_t op, FILE *const f) +{ + static int (*const checks[])(FILE *) = + { + [OP_UNREACHABLE] = check_unreachable, + [OP_NOP] = check_nop, + [OP_BLOCK] = check_block, + [OP_LOOP] = check_loop, + [OP_IF] = check_if, + [OP_ELSE] = check_else, + [OP_END] = check_end, + [OP_BR] = check_br, + [OP_BR_IF] = check_br_if, + [OP_BR_TABLE] = check_br_table, + [OP_RETURN] = check_return, + [OP_CALL] = check_call, + [OP_CALL_INDIRECT] = check_call_indirect, + [OP_GET_LOCAL] = check_get_local, + [OP_SET_LOCAL] = check_set_local, + [OP_TEE_LOCAL] = check_tee_local, + [OP_GET_GLOBAL] = check_get_global, + [OP_SET_GLOBAL] = check_set_global, + [OP_I32_LOAD] = check_i32_load, + [OP_I32_STORE] = check_i32_store, + [OP_I32_CONST] = check_i32_const, + [OP_I64_CONST] = check_i64_const, + [OP_F32_CONST] = check_f32_const, + [OP_F64_CONST] = check_f64_const, + [OP_I32_SUB] = check_i32_sub + }; + + if (op >= sizeof checks / sizeof *checks) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op); + return 1; + } + else if (!checks[op]) + { + LOG("%s: unsupported opcode %#" PRIx8 "\n", __func__, op); + return 1; + } + + return checks[op](f); +} + +int interp_start(const struct nw_interp_cfg *const cfg, FILE *const f, + struct nw_interp *const i) +{ + *i = (const struct nw_interp) + { + .f = f, + .cfg = *cfg + }; + + return 0; +} + +void *interp_stackptr(const struct nw_interp *const i) +{ + return (char *)i->cfg.stack.buf + i->stack_i; +} + +static int push_frame(struct nw_interp *const i, + const struct nw_frame *const src) +{ + struct nw_frame *const dst = interp_stackptr(i); + + if (interp_stack_push(i, src, sizeof (*src))) + { + LOG("%s: interp_stack_push failed\n", __func__); + return -1; + } + else if (i->fp) + i->fp->next = dst; + + i->fp = dst; + return 0; +} + +static bool mul_overflow(const size_t a, const size_t b) +{ + return a && (a * b) / a != b; +} + +static void init_i32(const varuint32 count, void *const p) +{ + for (int32_t *i = p; i - (const int32_t *)p < count; i++) + *i = 0; +} + +static void init_i64(const varuint32 count, void *const p) +{ + for (int64_t *i = p; i - (const int64_t *)p < count; i++) + *i = 0; +} + +static void init_f32(const varuint32 count, void *const p) +{ + for (float *i = p; i - (const float *)p < count; i++) + *i = 0; +} + +static void init_f64(const varuint32 count, void *const p) +{ + for (double *i = p; i - (const double *)p < count; i++) + *i = 0; +} + +static void init_locals(const enum value_type type, const varuint32 count, + void *const p) +{ + static void (*const init[])(varuint32, void *) = + { + [VALUE_TYPE_I32] = init_i32, + [VALUE_TYPE_I64] = init_i64, + [VALUE_TYPE_F32] = init_f32, + [VALUE_TYPE_F64] = init_f64 + }; + + init[type](count, p); +} + +static int push_locals(struct nw_interp *const i, + const struct nw_frame *const f) +{ + const enum value_type t = f->local_type; + const size_t typesz = get_type_size(t); + const unsigned long n = f->n_locals; + + if (mul_overflow(typesz, f->n_locals)) + { + LOG("%s: local variables size mul overflow", __func__); + i->exception = "mul overfllow"; + return -1; + } + + const size_t totalsz = typesz * n, max = i->cfg.stack.n; + + if (totalsz > max || i->stack_i > max - totalsz) + { + LOG("%s: cannot allocate locals\n", __func__); + i->exception = "stack overflow"; + return 1; + } + + init_locals(t, n, interp_stackptr(i)); + i->stack_i += totalsz; + return 0; +} + +static int do_push_labels(struct nw_interp *const i, + const struct nw_frame *const fr) +{ + size_t n = 0; + + for (;;) + { + + interp_check_opcode(); + } +} + +static int push_labels(struct nw_interp *const i, + const struct nw_frame *const fr) +{ + FILE *const f = i->f; + const long orig = ftell(f); + + if (orig < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (do_push_labels(i, fr)) + { + LOG("%s: do_push_labels failed\n", __func__); + return -1; + } + else if (fseek(f, orig, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return -1; +} + +int interp_push(struct nw_interp *const i, const struct nw_frame *const f) +{ + if (push_frame(i, f)) + { + LOG("%s: push_frame failed\n", __func__); + return -1; + } + else if (push_locals(i, f)) + { + LOG("%s: push_locals failed\n", __func__); + return -1; + } + else if (push_labels(i, f)) + { + LOG("%s: push_labels failed\n", __func__); + return -1; + } + + return 0; +} + +struct retval_priv +{ + enum value_type type; + + union + { + int32_t i32; + int64_t i64; + float f32; + double f64; + } u; +}; + +static void get_i32(const void *const buf, struct retval_priv *const rp) +{ + rp->u.i32 = *(((const int32_t *)buf) - 1); +} + +static void get_i64(const void *const buf, struct retval_priv *const rp) +{ + rp->u.i64 = *(((const int64_t *)buf) - 1); +} + +static void get_f32(const void *const buf, struct retval_priv *const rp) +{ + rp->u.f32 = *(((const float *)buf) - 1); +} + +static void get_f64(const void *const buf, struct retval_priv *const rp) +{ + rp->u.f64 = *(((const double *)buf) - 1); +} + +static int get_retval(struct nw_interp *const i, + struct retval_priv *const rp) +{ + const struct retval *const r = &i->fp->retval; + const size_t sz = get_type_size(r->type), max = i->cfg.stack.n; + + if (max < sz || i->stack_i >= max - sz) + { + static const char exc[] = "stack underflow"; + + LOG("%s: %s\n", __func__, exc); + i->exception = exc; + return -1; + } + + const void *const buf = (const char *)interp_stackptr(i) - sz; + static void (*const get[])(const void *, struct retval_priv *) = + { + [VALUE_TYPE_I32] = get_i32, + [VALUE_TYPE_I64] = get_i64, + [VALUE_TYPE_F32] = get_f32, + [VALUE_TYPE_F64] = get_f64 + }; + + get[r->type](buf, rp); + rp->type = r->type; + i->stack_i -= sz; + return 0; +} + +int interp_pop(struct nw_interp *const i) +{ + if (!i->fp) + { + static const char exc[] = "no frame pointer"; + + LOG("%s: %s\n", __func__, exc); + i->exception = exc; + return -1; + } + + const struct retval *const r = &i->fp->retval; + struct retval_priv rp; + + if (r->returns && get_retval(i, &rp)) + { + LOG("%s: get_retval failed\n", __func__); + return -1; + } + + const char *const stackptr = interp_stackptr(i); + const ptrdiff_t diff = stackptr - (const char *)i->fp; + + i->stack_i -= diff; + + if (!(i->fp = i->fp->prev)) + { + /* Entry point is always assumed to return int, which is + * defined as an i32 variable according to Wasm. */ + if (rp.type != VALUE_TYPE_I32) + { + LOG("%s: expected i32 return type, got %s\n", __func__, + value_type_tostr(rp.type)); + i->exception = "unexpected return type"; + return -1; + } + + i->retval = rp.u.i32; + i->exit = true; + } + + return 0; +} + +int interp_stack_push(struct nw_interp *const i, const void *const src, + const size_t n) +{ + void *const dst = interp_stackptr(i); + + if (i->cfg.stack.n < n || i->stack_i > i->cfg.stack.n - n) + { + LOG("%s: stack overflow\n", __func__); + i->exception = "stack overflow"; + return -1; + } + + memcpy(dst, src, n); + i->stack_i += n; + return 0; +} + +int interp_stack_pop(struct nw_interp *const i, void *const dst, + const size_t n) +{ + if (i->stack_i < n) + { + LOG("%s: stack underflow\n", __func__); + i->exception = "stack underflow"; + return -1; + } + + i->stack_i -= n; + memcpy(dst, interp_stackptr(i), n); + return 0; +} + +int interp_heap_store(struct nw_interp *const i, const size_t addr, + const void *const src, const size_t n) +{ + if (i->cfg.heap.n < n || addr > i->cfg.heap.n - n) + { + static const char exc[] = "heap overflow"; + + LOG("%s: %s\n", __func__, exc); + i->exception = exc; + return -1; + } + + void *const dst = (char *)i->cfg.heap.buf + addr; + + memcpy(dst, src, n); + return 0; +} + +int interp_heap_load(struct nw_interp *const i, const size_t addr, + void *const dst, const size_t n) +{ + const size_t max = i->cfg.heap.n; + + if (max < n || addr > max - n) + { + static const char exc[] = "heap out-of-bounds access"; + + LOG("%s: %s (%lu, max %zu)\n", __func__, exc, (unsigned long)addr, max); + i->exception = exc; + return -1; + } + + const void *const src = (const char *)i->cfg.heap.buf + addr; + + memcpy(dst, src, n); + return 0; +} diff --git a/src/leb128.c b/src/leb128.c new file mode 100644 index 0000000..3c3c8a5 --- /dev/null +++ b/src/leb128.c @@ -0,0 +1,71 @@ +/* + * 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 <nw/leb128.h> +#include <nw/log.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> + +/* The functions below are (somewhat heavily) modified versions of those + * provided by the wac project: https://github.com/kanaka/wac */ + +/* + * Copyright (C) Joel Martin <github@martintribe.org> + * The wac project is licensed under the MPL 2.0 (Mozilla Public License + * 2.0). The text of the MPL 2.0 license is included below and can be + * found at https://www.mozilla.org/MPL/2.0/ + */ + +static int read_LEB(FILE *const f, const unsigned maxbits, + const bool sign, unsigned long long *const out) +{ + unsigned long long result = 0; + unsigned shift = 0, bcnt = 0; + uint8_t byte; + + for (;;) + { + if (!fread(&byte, sizeof byte, 1, f)) + return -1; + + result |= (unsigned long long)(byte & 0x7f) << shift; + shift += 7; + + if (!(byte & 0x80)) + break; + else if (++bcnt > (maxbits + 7 - 1) / 7) + { + LOG("%s: overflow\n", __func__); + return -1; + } + } + + if (sign && (shift < maxbits) && (byte & 0x40)) + result |= -1ll << shift; + + *out = result; + return 0; +} + +int leb128_read_unsigned(FILE *const f, const unsigned maxbits, + unsigned long long *const out) +{ + return read_LEB(f, maxbits, false, out); +} + +int leb128_read_signed(FILE *const f, const unsigned maxbits, + long long *const out) +{ + unsigned long long value; + const int ret = read_LEB(f, maxbits, true, &value); + + *out = value; + return ret; +} diff --git a/src/load.c b/src/load.c new file mode 100644 index 0000000..2ba0fcc --- /dev/null +++ b/src/load.c @@ -0,0 +1,178 @@ +/* + * 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 <nw/log.h> +#include <nanowasm/nw.h> +#include <nw/sections.h> +#include <nw/types.h> +#include <errno.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int check_magic(FILE *const f) +{ + static const uint8_t m[] = {'\0', 'a', 's', 'm'}; + uint8_t magic[sizeof m]; + + if (!fread(magic, sizeof magic, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + else if (memcmp(magic, m, sizeof magic)) + { + LOG("%s: wrong magic bytes\n", __func__); + return -1; + } + + return 0; +} + +static int check_version(FILE *const f) +{ + static const uint8_t v[] = {1, 0, 0, 0}; + uint8_t version[sizeof v]; + + if (!fread(version, sizeof version, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(f), + ferror(f)); + return -1; + } + else if (memcmp(version, v, sizeof version)) + { + LOG("%s: wrong version\n", __func__); + return -1; + } + + return 0; +} + +static int load_section(const struct nw_mod_cfg *const cfg, + struct nw_mod *const m, FILE *const f) +{ + static int (*const fn[])(const struct section *, struct nw_mod *, + unsigned long) = + { + section_custom_check, + section_type_check, + section_import_check, + section_function_check, + section_table_check, + section_memory_check, + section_global_check, + section_export_check, + section_start_check, + section_element_check, + section_code_check, + section_data_check + }; + + const struct section s = + { + .cfg = cfg, + .f = f + }; + + varuint7 section; + varuint32 len; + int ret; + + if (varuint7_read(f, §ion)) + { + if (feof(f)) + return 0; + + LOG("%s: varuint7_read failed\n", __func__); + return 1; + } + else if (section >= sizeof fn / sizeof *fn) + { + LOG("%s: invalid section %u\n", __func__, (unsigned)section); + return 1; + } + else if (varuint32_read(f, &len)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if ((ret = fn[section](&s, m, len))) + LOG("%s: failed to load section %u\n", __func__, (unsigned)section); + + return ret; +} + +static int load_sections(const struct nw_mod_cfg *const cfg, + struct nw_mod *const m, FILE *const f) +{ + while (!feof(f)) + if (load_section(cfg, m, f)) + { + LOG("%s: load_section failed\n", __func__); + return -1; + } + + return 0; +} + +static int load(const struct nw_mod_cfg *const cfg, struct nw_mod *const m, + FILE *const f) +{ + if (check_magic(f)) + { + LOG("%s: load_magic failed\n", __func__); + return -1; + } + else if (check_version(f)) + { + LOG("%s: check_version failed\n", __func__); + return -1; + } + else if (load_sections(cfg, m, f)) + { + LOG("%s: load_sections failed\n", __func__); + return -1; + } + + return 0; +} + +int nw_load(const struct nw_mod_cfg *const cfg, struct nw_mod *const m) +{ + int ret = -1; + FILE *const f = fopen(cfg->path, "rb"); + + *m = (const struct nw_mod){.cfg = *cfg}; + + if (!f) + { + LOG("%s: fopen(3) %s: %s\n", __func__, cfg->path, strerror(errno)); + goto end; + } + else if (load(cfg, m, f)) + { + LOG("%s: load failed\n", __func__); + goto end; + } + + ret = 0; + +end: + if (f && fclose(f)) + { + LOG("%s: fclose(3) %s: %s\n", __func__, cfg->path, strerror(errno)); + ret = -1; + } + + return ret; +} diff --git a/src/op/CMakeLists.txt b/src/op/CMakeLists.txt new file mode 100644 index 0000000..7bf27de --- /dev/null +++ b/src/op/CMakeLists.txt @@ -0,0 +1,13 @@ +# 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/. + +add_subdirectory(call) +add_subdirectory(constants) +add_subdirectory(control_flow) +add_subdirectory(memory) +add_subdirectory(numeric) +add_subdirectory(variable_access) diff --git a/src/op/call/CMakeLists.txt b/src/op/call/CMakeLists.txt new file mode 100644 index 0000000..527f35d --- /dev/null +++ b/src/op/call/CMakeLists.txt @@ -0,0 +1,11 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + call.c + call_indirect.c +) diff --git a/src/op/call/call.c b/src/op/call/call.c new file mode 100644 index 0000000..19355ca --- /dev/null +++ b/src/op/call/call.c @@ -0,0 +1,79 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/search.h> +#include <nw/sections.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +static int call(struct nw_interp *const i, const varuint32 function_index) +{ + FILE *const f = i->f; + const struct nw_mod *const m = i->cfg.m; + struct nw_frame fr = {0}; + struct search_fn fn; + + /* TODO: treat import functions. */ + if (search_fn(function_index, m, f, &fn)) + { + LOG("%s: search_fn failed\n", __func__); + return -1; + } + else if (section_type_push(f, m, fn.index, &fr)) + { + LOG("%s: section_type_push failed\n", __func__); + return -1; + } + else if (section_code_push(f, fn.start, &fr)) + { + LOG("%s: section_code_push failed\n", __func__); + return -1; + } + else if (interp_push(i, &fr)) + { + LOG("%s: interp_push failed\n", __func__); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 function_index; + + if (varuint32_read(f, &function_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (i && call(i, function_index)) + { + LOG("%s: call failed\n", __func__); + return -1; + } + + return 0; +} + +int op_call(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_call(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/call/call_indirect.c b/src/op/call/call_indirect.c new file mode 100644 index 0000000..bae8a57 --- /dev/null +++ b/src/op/call/call_indirect.c @@ -0,0 +1,49 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 type_index; + varuint1 reserved; + + if (varuint32_read(f, &type_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (varuint1_read(f, &reserved)) + { + LOG("%s: varuint1_read failed\n", __func__); + return 1; + } + else if (reserved) + { + LOG("%s: unexpected non-zero reserved value %u\n", + __func__, (unsigned)reserved); + return 1; + } + + return 0; +} + +int op_call_indirect(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_call_indirect(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/constants/CMakeLists.txt b/src/op/constants/CMakeLists.txt new file mode 100644 index 0000000..e5cc8c5 --- /dev/null +++ b/src/op/constants/CMakeLists.txt @@ -0,0 +1,13 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + f32_const.c + f64_const.c + i32_const.c + i64_const.c +) diff --git a/src/op/constants/f32_const.c b/src/op/constants/f32_const.c new file mode 100644 index 0000000..c0c51b9 --- /dev/null +++ b/src/op/constants/f32_const.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 value; + + if (varuint32_read(f, &value)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_f32_const(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_f32_const(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/constants/f64_const.c b/src/op/constants/f64_const.c new file mode 100644 index 0000000..d88398c --- /dev/null +++ b/src/op/constants/f64_const.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint64 value; + + if (varuint64_read(f, &value)) + { + LOG("%s: varuint64_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_f64_const(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_f64_const(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/constants/i32_const.c b/src/op/constants/i32_const.c new file mode 100644 index 0000000..19161d3 --- /dev/null +++ b/src/op/constants/i32_const.c @@ -0,0 +1,51 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <nw/interp.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +static int set_value(struct nw_interp *const i, const int32_t src) +{ + return interp_stack_push(i, &src, sizeof src); +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint32 value; + + if (varint32_read(f, &value)) + { + LOG("%s: varint32_read failed\n", __func__); + return -1; + } + else if (i && set_value(i, value)) + { + LOG("%s: set_value failed\n", __func__); + return -1; + } + + return 0; +} + +int op_i32_const(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_i32_const(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/constants/i64_const.c b/src/op/constants/i64_const.c new file mode 100644 index 0000000..53f4237 --- /dev/null +++ b/src/op/constants/i64_const.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint64 value; + + if (varint64_read(f, &value)) + { + LOG("%s: varint32_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_i64_const(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_i64_const(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/CMakeLists.txt b/src/op/control_flow/CMakeLists.txt new file mode 100644 index 0000000..832f09e --- /dev/null +++ b/src/op/control_flow/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + block.c + br.c + br_if.c + br_table.c + else.c + end.c + if.c + loop.c + nop.c + return.c + unreachable.c +) diff --git a/src/op/control_flow/block.c b/src/op/control_flow/block.c new file mode 100644 index 0000000..56f72f1 --- /dev/null +++ b/src/op/control_flow/block.c @@ -0,0 +1,75 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/types.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +static int push(struct nw_interp *const i, FILE *const f) +{ + const long pc = ftell(f); + + if (pc < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + i->exception = "I/O error"; + return -1; + } + + struct nw_frame *const fp = i->fp; + struct nw_block *const p = interp_stackptr(i); + const struct nw_block b = + { + .pc = pc, + .prev = fp->last_block + }; + + if (interp_stack_push(i, &b, sizeof b)) + { + LOG("%s: interp_stack_push failed\n", __func__); + return -1; + } + + fp->last_block = p; + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint7 sig; + + if (varint7_read(f, &sig)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (i && push(i, f)) + { + LOG("%s: push failed\n", __func__); + return -1; + } + + return 0; +} + +int op_block(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_block(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/br.c b/src/op/control_flow/br.c new file mode 100644 index 0000000..df3aacb --- /dev/null +++ b/src/op/control_flow/br.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 relative_depth; + + if (varuint32_read(f, &relative_depth)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_br(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_br(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/br_if.c b/src/op/control_flow/br_if.c new file mode 100644 index 0000000..25bfdee --- /dev/null +++ b/src/op/control_flow/br_if.c @@ -0,0 +1,95 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/types.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <string.h> + +static int branch(struct nw_interp *const i, const varuint32 relative_depth) +{ + const struct nw_block *b = i->fp->last_block; + static const char exc[] = "relative depth and block count mismatch"; + + for (varuint32 d = 0; b && d < relative_depth; d++) + b = b->prev; + + if (!b) + { + LOG("%s: %s\n", __func__, exc); + i->exception = exc; + return -1; + } + + if (fseek(i->f, b->pc, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + i->exception = "I/O error"; + return -1; + } + + return 0; +} + +static bool cond(struct nw_interp *const i) +{ + int32_t value; + + if (interp_stack_pop(i, &value, sizeof value)) + { + LOG("%s: interp_stack_pop failed\n", __func__); + return -1; + } + + return value; +} + +static int br_if(struct nw_interp *const i, const varuint32 relative_depth) +{ + if (cond(i) && branch(i, relative_depth)) + { + LOG("%s: branch failed\n", __func__); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 relative_depth; + + if (varuint32_read(f, &relative_depth)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (i && br_if(i, relative_depth)) + { + LOG("%s: br_if failed\n", __func__); + return -1; + } + + return 0; +} + +int op_br_if(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_br_if(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/br_table.c b/src/op/control_flow/br_table.c new file mode 100644 index 0000000..837cdcd --- /dev/null +++ b/src/op/control_flow/br_table.c @@ -0,0 +1,56 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 target_count, default_target; + + if (varuint32_read(f, &target_count)) + { + LOG("%s: varuint32_read target_count failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < target_count; i++) + { + varuint32 target_table; + + if (varuint32_read(f, &target_table)) + { + LOG("%s: varuint32_read target_table failed\n", + __func__); + return -1; + } + } + + if (varuint32_read(f, &default_target)) + { + LOG("%s: varuint32_read default_target failed\n", __func__); + return -1; + } + + return 0; +} + +int op_br_table(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_br_table(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/else.c b/src/op/control_flow/else.c new file mode 100644 index 0000000..fb839f0 --- /dev/null +++ b/src/op/control_flow/else.c @@ -0,0 +1,21 @@ +/* + * 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 <nw/ops.h> +#include <nanowasm/nw.h> + +int op_else(struct nw_interp *const i) +{ + return -1; +} + +int check_else(FILE *const f) +{ + return 0; +} diff --git a/src/op/control_flow/end.c b/src/op/control_flow/end.c new file mode 100644 index 0000000..5f193d9 --- /dev/null +++ b/src/op/control_flow/end.c @@ -0,0 +1,24 @@ +/* + * 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 <nw/ops.h> +#include <nanowasm/nw.h> +#include <stdbool.h> + +int op_end(struct nw_interp *const i) +{ + /* TODO: this is not correct. end can appear anywhere in a function. */ + i->exit = true; + return 0; +} + +int check_end(FILE *const f) +{ + return 0; +} diff --git a/src/op/control_flow/if.c b/src/op/control_flow/if.c new file mode 100644 index 0000000..1d5e838 --- /dev/null +++ b/src/op/control_flow/if.c @@ -0,0 +1,36 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint7 sig; + + if (varint7_read(f, &sig)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_if(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_if(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/loop.c b/src/op/control_flow/loop.c new file mode 100644 index 0000000..5020f23 --- /dev/null +++ b/src/op/control_flow/loop.c @@ -0,0 +1,36 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint7 sig; + + if (varint7_read(f, &sig)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + + return 0; +} + +int op_loop(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_loop(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/control_flow/nop.c b/src/op/control_flow/nop.c new file mode 100644 index 0000000..356b7b0 --- /dev/null +++ b/src/op/control_flow/nop.c @@ -0,0 +1,22 @@ +/* + * 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 <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> + +int op_nop(struct nw_interp *const i) +{ + return 0; +} + +int check_nop(FILE *const f) +{ + return 0; +} diff --git a/src/op/control_flow/return.c b/src/op/control_flow/return.c new file mode 100644 index 0000000..abddea7 --- /dev/null +++ b/src/op/control_flow/return.c @@ -0,0 +1,30 @@ +/* + * 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 <nw/log.h> +#include <nw/interp.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <stdio.h> + +int op_return(struct nw_interp *const i) +{ + if (interp_pop(i)) + { + LOG("%s: interp_pop failed\n", __func__); + return -1; + } + + return 0; +} + +int check_return(FILE *const f) +{ + return 0; +} diff --git a/src/op/control_flow/unreachable.c b/src/op/control_flow/unreachable.c new file mode 100644 index 0000000..8f1982c --- /dev/null +++ b/src/op/control_flow/unreachable.c @@ -0,0 +1,25 @@ +/* + * 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 <nw/ops.h> +#include <nanowasm/nw.h> +#include <stdbool.h> +#include <stdio.h> + +int op_unreachable(struct nw_interp *const i) +{ + i->exception = "Unreachable instruction"; + i->exit = true; + return 1; +} + +int check_unreachable(FILE *const f) +{ + return 0; +} diff --git a/src/op/memory/CMakeLists.txt b/src/op/memory/CMakeLists.txt new file mode 100644 index 0000000..ca4ba5a --- /dev/null +++ b/src/op/memory/CMakeLists.txt @@ -0,0 +1,12 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + current_memory.c + i32_load.c + i32_store.c +) diff --git a/src/op/memory/current_memory.c b/src/op/memory/current_memory.c new file mode 100644 index 0000000..c67d425 --- /dev/null +++ b/src/op/memory/current_memory.c @@ -0,0 +1,44 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint1 reserved; + + if (varuint1_read(f, &reserved)) + { + LOG("%s: varuint1_read failed\n", __func__); + return 1; + } + else if (reserved) + { + LOG("%s: unexpected non-zero reserved value %u\n", + __func__, (unsigned)reserved); + return 1; + } + + return 0; +} + +int op_current_memory(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_current_memory(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/memory/i32_load.c b/src/op/memory/i32_load.c new file mode 100644 index 0000000..04dbec7 --- /dev/null +++ b/src/op/memory/i32_load.c @@ -0,0 +1,76 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int load(struct nw_interp *const i, const varuint32 flags, + const varuint32 offset) +{ + enum {ALIGN = 2}; + int32_t value; + + if (flags != ALIGN) + { + LOG("%s: expected 32-bit alignment (%d), got %lu\n", + __func__, ALIGN, (unsigned long)flags); + i->exception = "unaligned access"; + return -1; + } + else if (interp_heap_load(i, offset, &value, sizeof value)) + { + LOG("%s: interp_heap_load failed\n", __func__); + return -1; + } + else if (interp_stack_push(i, &value, sizeof value)) + { + LOG("%s: interp_stack_push failed\n", __func__); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varint32 flags, offset; + + if (varint32_read(f, &flags)) + { + LOG("%s: varint32_read flags failed\n", __func__); + return 1; + } + else if (varint32_read(f, &offset)) + { + LOG("%s: varint32_read offset failed\n", __func__); + return 1; + } + else if (i && load(i, flags, offset)) + { + LOG("%s: load failed\n", __func__); + return -1; + } + + return 0; +} + +int op_i32_load(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_i32_load(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/memory/i32_store.c b/src/op/memory/i32_store.c new file mode 100644 index 0000000..212cd86 --- /dev/null +++ b/src/op/memory/i32_store.c @@ -0,0 +1,125 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <nw/interp.h> +#include <stdint.h> +#include <stddef.h> +#include <stdio.h> + +static int store(struct nw_interp *const i, const varuint32 flags, + const varuint32 offset) +{ + enum {ALIGN = 2}; + + if (flags != ALIGN) + { + LOG("%s: expected 32-bit alignment (%d), got %lu\n", + __func__, ALIGN, (unsigned long)flags); + i->exception = "unaligned access"; + return -1; + } + + int32_t value, addr; + + if (interp_stack_pop(i, &value, sizeof value)) + { + LOG("%s: inter_stack_pop value failed\n", __func__); + return -1; + } + else if (interp_stack_pop(i, &addr, sizeof addr)) + { + LOG("%s: inter_stack_pop addr failed\n", __func__); + return -1; + } + else if (offset > SIZE_MAX - addr) + { + static const char exc[] = "offset address overflow"; + + LOG("%s: %s\n", __func__, exc); + i->exception = exc; + return -1; + } + else if (interp_heap_store(i, offset + addr, &value, sizeof value)) + { + LOG("%s: interp_heap_store failed\n", __func__); + return -1; + } + + return 0; +#if 0 + stack_i -= sizeof addr; + + if (max < addr || offset >= max - addr) + { + LOG("%s: heap overflow\n", __func__); + i->exception = "heap overflow"; + return -1; + } + + const size_t raddr = offset + addr; + + if (raddr % sizeof addr) + { + LOG("%s: unaligned memory address\n", __func__); + i->exception = "unaligned access"; + return -1; + } + else if (stack_i < sizeof (uint32_t)) + { + LOG("%s: stack underflow (value)\n", __func__); + i->exception = "stack underflow"; + return -1; + } + + const int32_t value = htoni32(*stackptr); + void *dst = &((char *)i->cfg.heap.buf)[raddr]; + + *(int32_t *)dst = value; + stack_i -= sizeof addr; + i->stack_i = stack_i; + return 0; +#endif +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 flags, offset; + + if (varuint32_read(f, &flags)) + { + LOG("%s: varint32_read flags failed\n", __func__); + return -1; + } + else if (varuint32_read(f, &offset)) + { + LOG("%s: varint32_read offset failed\n", __func__); + return -1; + } + else if (i && store(i, flags, offset)) + { + LOG("%s: store failed\n", __func__); + return -1; + } + + return 0; +} + +int op_i32_store(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_i32_store(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/numeric/CMakeLists.txt b/src/op/numeric/CMakeLists.txt new file mode 100644 index 0000000..a259506 --- /dev/null +++ b/src/op/numeric/CMakeLists.txt @@ -0,0 +1,10 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + i32_sub.c +) diff --git a/src/op/numeric/i32_sub.c b/src/op/numeric/i32_sub.c new file mode 100644 index 0000000..c3d1352 --- /dev/null +++ b/src/op/numeric/i32_sub.c @@ -0,0 +1,66 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/types.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +/* Inspired by "INT32-C. Ensure that operations on signed integers do + * not result in overflow". */ +static bool signed_overflow(const int32_t a, const int32_t b) +{ + return (b > 0 && a < INT32_MIN + b) + || (b < 0 && a > INT32_MAX + b); +} + +int op_i32_sub(struct nw_interp *const i) +{ + int32_t op1, op2; + + if (interp_stack_pop(i, &op2, sizeof op2)) + { + LOG("%s: interp_stack_pop %s failed\n", __func__, "op2"); + return -1; + } + else if (interp_stack_pop(i, &op1, sizeof op1)) + { + LOG("%s: interp_stack_pop %s failed\n", __func__, "op1"); + return -1; + } + else if (signed_overflow(op2, op1)) + { + LOG("%s: signed integer overflow (op1=%" PRIi32 ", op2=%" PRIi32 ")\n", + __func__, op1, op2); + i->exception = "signed integer overflow"; + return 1; + } + + const int32_t result = op2 - op1; + + if (interp_stack_push(i, &result, sizeof result)) + { + LOG("%s: interp_stack_push failed\n", __func__); + return -1; + } + + return 0; +} + +int check_i32_sub(FILE *const f) +{ + return 0; +} diff --git a/src/op/variable_access/CMakeLists.txt b/src/op/variable_access/CMakeLists.txt new file mode 100644 index 0000000..314b5bf --- /dev/null +++ b/src/op/variable_access/CMakeLists.txt @@ -0,0 +1,14 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + get_global.c + get_local.c + set_global.c + set_local.c + tee_local.c +) diff --git a/src/op/variable_access/get_global.c b/src/op/variable_access/get_global.c new file mode 100644 index 0000000..d38f2d0 --- /dev/null +++ b/src/op/variable_access/get_global.c @@ -0,0 +1,100 @@ +/* + * 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 <nw/log.h> +#include <nw/interp.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +static int get_i32(struct nw_interp *const i, const void *const src) +{ + return interp_stack_push(i, src, sizeof (int32_t)); +} + +static int get_i64(struct nw_interp *const i, const void *const src) +{ + return interp_stack_push(i, src, sizeof (int64_t)); +} + +static int get_f32(struct nw_interp *const i, const void *const src) +{ + return interp_stack_push(i, src, sizeof (float)); +} + +static int get_f64(struct nw_interp *const i, const void *const src) +{ + return interp_stack_push(i, src, sizeof (double)); +} + +static int get_global(struct nw_interp *const i, const varuint32 global_index) +{ + struct nw_gframe *gfr; + varuint32 idx; + + for (idx = global_index, gfr = i->gfp; gfr; gfr = gfr->next) + if (idx == global_index) + break; + + if (!gfr) + { + LOG("%s: cannot access global variable %lu\n", __func__, + (unsigned long)global_index); + i->exception = "global variable index out of bounds"; + return -1; + } + + static int (*const get[])(struct nw_interp *, const void *) = + { + [VALUE_TYPE_I32] = get_i32, + [VALUE_TYPE_I64] = get_i64, + [VALUE_TYPE_F32] = get_f32, + [VALUE_TYPE_F64] = get_f64, + }; + + if (get[gfr->type](i, gfr + 1)) + { + LOG("%s: get type %s failed\n", __func__, value_type_tostr(gfr->type)); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 global_index; + + if (varuint32_read(f, &global_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (i && get_global(i, global_index)) + { + LOG("%s: get_global failed\n", __func__); + return -1; + } + + return 0; +} + +int op_get_global(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_get_global(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/variable_access/get_local.c b/src/op/variable_access/get_local.c new file mode 100644 index 0000000..745a2e7 --- /dev/null +++ b/src/op/variable_access/get_local.c @@ -0,0 +1,112 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <nw/interp.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +static int get_i32(struct nw_interp *const i, const void *const data, + const varuint32 idx) +{ + const int32_t *const src = (const int32_t *)data + idx; + + return interp_stack_push(i, src, sizeof (*src)); +} + +static int get_i64(struct nw_interp *const i, const void *const data, + const varuint32 idx) +{ + const int64_t *const src = (const int64_t *)data + idx; + + return interp_stack_push(i, src, sizeof (*src)); +} + +static int get_f32(struct nw_interp *const i, const void *const data, + const varuint32 idx) +{ + const float *const src = (const float *)data + idx; + + return interp_stack_push(i, src, sizeof (*src)); +} + +static int get_f64(struct nw_interp *const i, const void *const data, + const varuint32 idx) +{ + const double *const src = (const double *)data + idx; + + return interp_stack_push(i, src, sizeof (*src)); +} + +static int get_local(const varuint32 local_index, struct nw_interp *const i) +{ + struct nw_frame *fr; + varuint32 idx; + + for (idx = local_index, fr = i->fp; fr; fr = fr->next) + if (idx < fr->n_locals) + break; + + if (!fr) + { + LOG("%s: cannot access local variable %lu\n", __func__, + (unsigned long)local_index); + i->exception = "local variable index out of bounds"; + return -1; + } + + static int (*const get[])(struct nw_interp *, const void *, varuint32) = + { + [VALUE_TYPE_I32] = get_i32, + [VALUE_TYPE_I64] = get_i64, + [VALUE_TYPE_F32] = get_f32, + [VALUE_TYPE_F64] = get_f64, + }; + + if (get[fr->local_type](i, fr + 1, idx)) + { + LOG("%s: get type %d failed\n", __func__, fr->local_type); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 local_index; + + if (varuint32_read(f, &local_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (i && get_local(local_index, i)) + { + LOG("%s: get_local failed\n", __func__); + return -1; + } + + return 0; +} + +int op_get_local(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_get_local(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/variable_access/set_global.c b/src/op/variable_access/set_global.c new file mode 100644 index 0000000..d1229c5 --- /dev/null +++ b/src/op/variable_access/set_global.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 global_index; + + if (varuint32_read(f, &global_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + + return 0; +} + +int op_set_global(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_set_global(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/variable_access/set_local.c b/src/op/variable_access/set_local.c new file mode 100644 index 0000000..8cf6440 --- /dev/null +++ b/src/op/variable_access/set_local.c @@ -0,0 +1,124 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +static int set_i32(struct nw_interp *const i, void *const data, + const varuint32 idx) +{ + const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i; + const int32_t *const src = (const int32_t *)stackptr - 1; + int32_t *const dst = (int32_t *)data + idx; + + *dst = *src; + return 0; +} + +static int set_i64(struct nw_interp *const i, void *const data, + const varuint32 idx) +{ + const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i; + const int64_t *const src = (const int64_t *)stackptr - 1; + int64_t *const dst = (int64_t *)data + idx; + + *dst = *src; + return 0; +} + +static int set_f32(struct nw_interp *const i, void *const data, + const varuint32 idx) +{ + const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i; + const float *const src = (const float *)stackptr - 1; + float *const dst = (float *)data + idx; + + *dst = *src; + return 0; +} + +static int set_f64(struct nw_interp *const i, void *const data, + const varuint32 idx) +{ + const void *const stackptr = (const char *)i->cfg.stack.buf + i->stack_i; + const double *const src = (const double *)stackptr - 1; + double *const dst = (double *)data + idx; + + *dst = *src; + return 0; +} + +static int set_local(const varuint32 local_index, struct nw_interp *const i) +{ + struct nw_frame *fr; + varuint32 idx; + + for (idx = local_index, fr = i->fp; fr; fr = fr->next) + if (idx < fr->n_locals) + break; + + if (!fr) + { + LOG("%s: cannot access local variable %lu\n", __func__, + (unsigned long)local_index); + i->exception = "local variable index out of bounds"; + return -1; + } + + static int (*const set[])(struct nw_interp *, void *, varuint32) = + { + [VALUE_TYPE_I32] = set_i32, + [VALUE_TYPE_I64] = set_i64, + [VALUE_TYPE_F32] = set_f32, + [VALUE_TYPE_F64] = set_f64, + }; + + if (set[fr->local_type](i, fr + 1, idx)) + { + LOG("%s: set type %d failed\n", __func__, fr->local_type); + return -1; + } + + return 0; +} + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 local_index; + + if (varuint32_read(f, &local_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (i && set_local(local_index, i)) + { + LOG("%s: set_local failed\n", __func__); + return -1; + } + + return 0; +} + +int op_set_local(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_set_local(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/op/variable_access/tee_local.c b/src/op/variable_access/tee_local.c new file mode 100644 index 0000000..00d7832 --- /dev/null +++ b/src/op/variable_access/tee_local.c @@ -0,0 +1,38 @@ +/* + * 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 <nw/log.h> +#include <nw/ops.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <stddef.h> +#include <stdio.h> + +static int op(struct nw_interp *const i, FILE *const f) +{ + varuint32 local_index; + + if (varuint32_read(f, &local_index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + + return 0; +} + +int op_tee_local(struct nw_interp *const i) +{ + return op(i, i->f); +} + +int check_tee_local(FILE *const f) +{ + return op(NULL, f); +} diff --git a/src/run.c b/src/run.c new file mode 100644 index 0000000..e1cca7d --- /dev/null +++ b/src/run.c @@ -0,0 +1,42 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/search.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +enum nw_state nw_run(struct nw_inst *const i) +{ + struct nw_interp *const in = &i->interp; + union nw_inst_state *const s = &i->state; + + if (interp_run(in)) + { + LOG("%s: interp_run failed\n", __func__); + + if (in->exception) + { + s->exception = in->exception; + return NW_STATE_EXCEPTION; + } + + return NW_STATE_FATAL; + } + else if (in->exit) + { + s->retval = in->retval; + return NW_STATE_RETURNED; + } + + return NW_STATE_AGAIN; +} diff --git a/src/search.c b/src/search.c new file mode 100644 index 0000000..622b74c --- /dev/null +++ b/src/search.c @@ -0,0 +1,181 @@ +/* + * 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 <nw/search.h> +#include <nw/log.h> +#include <nw/fstring.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> + +static int ensure_export(const char *const fn, + const struct nw_mod *const m, FILE *const f, varuint32 *const out) +{ + if (!m->sections.export) + { + LOG("%s: export section not found", __func__); + return -1; + } + else if (fseek(f, m->sections.export, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + { + varuint32 len; + bool found = false; + + if (varuint32_read(f, &len)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (len != strlen(fn)) + { + if (fseek(f, len, SEEK_CUR)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + } + else if (!fstrcmp(fn, f, false)) + found = true; + + uint8_t kind; + + if (!fread(&kind, sizeof kind, 1, f)) + { + LOG("%s: fread(3) failed: feof=%d, ferror=%d\n", __func__, + feof(f), ferror(f)); + return -1; + } + + varuint32 index; + + if (varuint32_read(f, &index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (found) + { + if (kind != NW_KIND_FUNCTION) + { + LOG("%s: expected kind %d, got %d\n", __func__, + NW_KIND_FUNCTION, kind); + return -1; + } + + *out = index; + return 0; + } + } + + return -1; +} + +static int get_function_index(const struct nw_mod *const m, + const varuint32 index, varuint32 *const findex) +{ + const size_t n = m->cfg.n_imports; + + if (index < n) + { + LOG("%s: index (%lu) must be greater than number of imports (%zu)\n", + __func__, (unsigned long)index, n); + return -1; + } + + *findex = index - n; + return 0; +} + +static int get_fn_start(const struct nw_mod *const m, FILE *const f, + const varuint32 index, struct search_fn *const out) +{ + const struct section s = + { + .cfg = &m->cfg, + .f = f + }; + + struct section_function sf; + struct section_code c; + + if (section_function(&s, m, index, &sf)) + { + LOG("%s: section_function failed\n", __func__); + return -1; + } + else if (section_code(&s, m, index, &c)) + { + LOG("%s: section_code failed\n", __func__); + return -1; + } + + out->start = c.start; + out->index = index; + return 0; +} + +int search_exported_fn(const char *const fn, const struct nw_mod *const m, + FILE *const f, struct search_fn *const out) +{ + varuint32 index, findex; + + if (ensure_export(fn, m, f, &index)) + { + LOG("%s: %s not an exported function\n", __func__, fn); + return -1; + } + else if (get_function_index(m, index, &findex)) + { + LOG("%s: get_function_index failed\n", __func__); + return -1; + } + else if (get_fn_start(m, f, findex, out)) + { + LOG("%s: get_fn_start %s failed\n", __func__, fn); + return -1; + } + + return 0; +} + +int search_fn(const varuint32 index, const struct nw_mod *const m, + FILE *const f, struct search_fn *const out) +{ + varuint32 findex; + + if (get_function_index(m, index, &findex)) + { + LOG("%s: get_function_index failed\n", __func__); + return -1; + } + else if (get_fn_start(m, f, findex, out)) + { + LOG("%s: get_fn_start failed\n", __func__); + return -1; + } + + return 0; +} diff --git a/src/section/CMakeLists.txt b/src/section/CMakeLists.txt new file mode 100644 index 0000000..772dd3f --- /dev/null +++ b/src/section/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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/. + +target_sources(${PROJECT_NAME} PRIVATE + code.c + common.c + custom.c + data.c + element.c + export.c + function.c + global.c + import.c + memory.c + start.c + table.c + type.c +) diff --git a/src/section/code.c b/src/section/code.c new file mode 100644 index 0000000..da45047 --- /dev/null +++ b/src/section/code.c @@ -0,0 +1,429 @@ +/* + * 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 <nw/log.h> +#include <nanowasm/nw.h> +#include <nw/opcodes.h> +#include <nw/interp.h> +#include <nw/sections.h> +#include <nw/types.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +static int check_body(FILE *const f, const unsigned long n) +{ + unsigned long rem = n; + + while (rem) + { + const long before = ftell(f); + uint8_t byte; + + if (before < 0) + { + LOG("%s: ftell(3) before: %s\n", __func__, strerror(errno)); + return -1; + } + else if (!fread(&byte, sizeof byte, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + else if (rem == 1 && byte != OP_END) + { + LOG("%s: unexpected opcode %#" PRIx8 " at body end\n", + __func__, byte); + return -1; + } + else if (interp_check_opcode(byte, f)) + { + LOG("%s: interp_check_opcode failed\n", __func__); + return -1; + } + + const long after = ftell(f); + + if (after < 0) + { + LOG("%s: ftell(3) after: %s\n", __func__, strerror(errno)); + return -1; + } + + rem -= after - before; + } + + return 0; +} + +#if 0 + +static int push_locals(struct nw_interp *const i, + const struct nw_frame *const f) +{ + const enum value_type t = f->local_type; + const size_t typesz = get_type_size(t); + const unsigned long n = f->n_locals; + + if (mul_overflow(typesz, f->n_locals)) + { + LOG("%s: local variables size mul overflow", __func__); + i->exception = "mul overfllow"; + return -1; + } + + const size_t totalsz = typesz * n, max = i->cfg.stack.n; + + if (totalsz > max || i->stack_i > max - totalsz) + { + LOG("%s: cannot allocate locals\n", __func__); + i->exception = "stack overflow"; + return 1; + } + + init_locals(t, n, interp_stackptr(i)); + i->stack_i += totalsz; + return 0; +} + +static int push_block(struct nw_interp *const i) +{ + const long pc = ftell(i->f); + + if (pc < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const struct nw_block b = + { + .pc = pc + }; + + if (interp_stack_push(i, &b, sizeof b)) + { + LOG("%s: interp_stack_push failed\n", __func__); + return -1; + } + + struct nw_block *const p = (struct nw_block *)interp_stackptr(i) - 1; + + if (i->fp->block) + { + for (struct nw_block *b = i->fp->block; b; b = b->next) + if (!b->next) + { + b->next = p; + break; + } + } + else + i->fp->block = p; + + return 0; +} + +static int loop_push_blocks(struct nw_interp *const i) +{ + for (;;) + { + uint8_t op; + + if (!fread(&op, sizeof op, 1, i->f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, + feof(i->f), ferror(i->f)); + i->exception = "I/O error"; + return -1; + } + else if (op >= sizeof ops / sizeof *ops) + { + LOG("%s: invalid opcode %#" PRIx8 "\n", __func__, op); + i->exception = "invalid opcode"; + return -1; + } + else if (interp_check_opcode(op, i->f)) + { + LOG("%s: interp_check_opcode failed\n", __func__); + return -1; + } + + switch (op) + { + case OP_END: + return 0; + + case OP_BLOCK: + if (push_block(i)) + { + LOG("%s: push_block failed\n", __func__); + return -1; + } + + break; + + default: + break; + } + } +} + +static int push_blocks(struct nw_interp *const i, + const struct nw_frame *const fr) +{ + FILE *const f = i->f; + const long orig = ftell(f); + + if (orig < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (loop_push_blocks(i)) + { + LOG("%s: loop_push_blocks failed\n", __func__); + return -1; + } + else if (fseek(f, orig, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return 0; +} +#endif + +static int push_local_count(struct nw_frame *const f, const varuint32 count, + const varint7 type) +{ + enum value_type vtype; + + if (get_value_type(type, &vtype)) + { + LOG("%s: get_value_type failed\n", __func__); + return -1; + } + + f->n_locals = count; + f->local_type = vtype; + return 0; +} + +static int check_local(FILE *const f, struct nw_frame *const fr) +{ + varuint32 count; + varint7 type; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (varint7_read(f, &type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (fr && push_local_count(fr, count, type)) + { + LOG("%s: push_local_count failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_locals(FILE *const f, struct nw_frame *const fr) +{ + varuint32 local_count; + + if (varuint32_read(f, &local_count)) + { + LOG("%s: varuint32_read local_count failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < local_count; i++) + if (check_local(f, fr)) + { + LOG("%s: check_local failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_function_body(FILE *const f, long *const out) +{ + varuint32 body_size; + + if (varuint32_read(f, &body_size)) + { + LOG("%s: varuint32_read body_size failed\n", __func__); + return -1; + } + + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3) start: %s\n", __func__, strerror(errno)); + return -1; + } + else if (check_locals(f, NULL)) + { + LOG("%s: check_locals failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3) end: %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long consumed = end - start; + + if (consumed > body_size) + { + LOG("%s: exceeded function body size\n", __func__); + return -1; + } + else if (check_body(f, body_size - consumed)) + { + LOG("%s: check_body failed\n", __func__); + return -1; + } + + *out = start; + return 0; +} + +static int run(FILE *const f, const varuint32 idx, + struct section_code *const out) +{ + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + else if (count > ULONG_MAX - 1) + { + fprintf(stderr, "%s: count overflow\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + { + long start; + + if (check_function_body(f, &start)) + { + LOG("%s: check_function_body failed\n", __func__); + return -1; + } + else if (out && i == idx) + { + out->start = start; + return 0; + } + } + + return 0; +} + +int section_code_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.code) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (run(f, 0, NULL)) + { + LOG("%s: run failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + m->sections.code = start; + return 0; +} + +int section_code(const struct section *const s, const struct nw_mod *const m, + const varuint32 idx, struct section_code *const out) +{ + const long offset = m->sections.code; + + if (offset <= 0) + { + LOG("%s: code section not found", __func__); + return -1; + } + else if (fseek(s->f, offset, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return run(s->f, idx, out); +} + +int section_code_push(FILE *const f, const long pc, struct nw_frame *const fr) +{ + if (fseek(f, pc, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check_locals(f, fr)) + { + LOG("%s: check_local failed\n", __func__); + return -1; + } + + return 0; +} diff --git a/src/section/common.c b/src/section/common.c new file mode 100644 index 0000000..b768470 --- /dev/null +++ b/src/section/common.c @@ -0,0 +1,150 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nw/types.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +int check_resizable_limits(FILE *const f, struct resizable_limits *const r) +{ + varuint1 flags; + varuint32 initial; + + *r = (const struct resizable_limits){0}; + + if (varuint1_read(f, &flags)) + { + LOG("%s: varuint1_read failed\n", __func__); + return -1; + } + else if (varuint32_read(f, &initial)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + if (flags) + { + varuint32 maximum; + + if (varuint32_read(f, &maximum)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + r->max = maximum; + r->max_available = true; + } + + static const unsigned long page_size = 64lu * 1024lu; + + if (initial > UINT32_MAX / page_size) + { + LOG("%s: initial size (%lu) overflow \n", __func__, + (unsigned long)initial); + return 1; + } + + r->sz = initial * page_size; + return 0; +} + +size_t get_type_size(const enum value_type type) +{ + static const size_t list[] = + { + [VALUE_TYPE_I32] = sizeof (int32_t), + [VALUE_TYPE_I64] = sizeof (int64_t), + [VALUE_TYPE_F32] = sizeof (float), + [VALUE_TYPE_F64] = sizeof (double) + }; + + return list[type]; +} + +enum +{ + I32 = 0x7f, + I64 = 0x7e, + F32 = 0x7d, + F64 = 0x7c, + ANYFUNC = 0x70, + FUNC = 0x60, + BLOCK_TYPE = 0x40 +}; + +int get_value_type(const varint7 type, enum value_type *const vtype) +{ + static const struct size + { + varint7 type; + enum value_type vtype; + } sizes[] = + { + {.type = I32, .vtype = VALUE_TYPE_I32}, + {.type = I64, .vtype = VALUE_TYPE_I64}, + {.type = F32, .vtype = VALUE_TYPE_F32}, + {.type = F64, .vtype = VALUE_TYPE_F64}, + /* TODO: check this. */ + {.type = ANYFUNC, .vtype = VALUE_TYPE_I32}, + /* TODO: check this. */ + {.type = FUNC, .vtype = VALUE_TYPE_I32}, + /* TODO: check this. */ + {.type = BLOCK_TYPE, .vtype = VALUE_TYPE_I32} + }; + + for (size_t i = 0; i < sizeof sizes / sizeof *sizes; i++) + { + const struct size *const s = &sizes[i]; + + if (type == s->type) + { + *vtype = s->vtype; + return 0; + } + } + + LOG("%s: unknown type %#hhx\n", __func__, (char)type); + return -1; +} + +static int32_t swap_i32(const int32_t in) +{ + const int8_t *const p = (const int8_t *)∈ + + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +int32_t htoni32(const int32_t in) +{ + return swap_i32(in); +} + +int32_t ntohi32(const int32_t in) +{ + return swap_i32(in); +} + +const char *value_type_tostr(const enum value_type v) +{ + static const char *const s[] = + { +#define X(x) [x] = #x, + VALUE_TYPES +#undef X + }; + + return s[v]; +} diff --git a/src/section/custom.c b/src/section/custom.c new file mode 100644 index 0000000..fe06b74 --- /dev/null +++ b/src/section/custom.c @@ -0,0 +1,30 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int section_custom_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (fseek(f, len, SEEK_CUR)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/section/data.c b/src/section/data.c new file mode 100644 index 0000000..6ea12d6 --- /dev/null +++ b/src/section/data.c @@ -0,0 +1,18 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/sections.h> +#include <nw/types.h> + +int section_data_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + return -1; +} diff --git a/src/section/element.c b/src/section/element.c new file mode 100644 index 0000000..39bd5b7 --- /dev/null +++ b/src/section/element.c @@ -0,0 +1,17 @@ +/* + * 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 <nw/sections.h> +#include <nanowasm/nw.h> + +int section_element_check(const struct section *const s, + struct nw_mod *const m, const unsigned long len) +{ + return -1; +} diff --git a/src/section/export.c b/src/section/export.c new file mode 100644 index 0000000..0948067 --- /dev/null +++ b/src/section/export.c @@ -0,0 +1,161 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +static int check_string(FILE *const f) +{ + varuint32 len; + + if (varuint32_read(f, &len)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < len; i++) + { + uint8_t byte; + + if (!fread(&byte, sizeof byte, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + } + + return 0; +} + +static int check_kind(FILE *const f) +{ + uint8_t kind; + + if (!fread(&kind, sizeof kind, 1, f)) + { + LOG("%s: fread(3) failed: feof=%d, ferror=%d\n", __func__, + feof(f), ferror(f)); + return -1; + } + + return 0; +} + +static int check_index(FILE *const f) +{ + varuint32 index; + + if (varuint32_read(f, &index)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_export_entry(FILE *const f) +{ + if (check_string(f)) + { + LOG("%s: check_string failed\n", __func__); + return -1; + } + else if (check_kind(f)) + { + LOG("%s: check_kind failed\n", __func__); + return -1; + } + else if (check_index(f)) + { + LOG("%s: check_index failed\n", __func__); + return -1; + } + + return 0; +} + +static int check(FILE *const f, const unsigned long len) +{ + const long start = ftell(f); + varuint32 count; + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (check_export_entry(f)) + { + LOG("%s: check_export_entry failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_export_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.export) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check(f, len)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.export = offset; + return 0; +} diff --git a/src/section/function.c b/src/section/function.c new file mode 100644 index 0000000..3ffed36 --- /dev/null +++ b/src/section/function.c @@ -0,0 +1,127 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +static int run(FILE *const f, const varuint32 idx, + struct section_function *const out) +{ + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read count failed\n", __func__); + return -1; + } + else if (count > ULONG_MAX - 1) + { + fprintf(stderr, "%s: count overflow\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + { + varuint32 type; + + if (varuint32_read(f, &type)) + { + LOG("%s: varuint32_read type failed\n", __func__); + return -1; + } + else if (out && i == idx) + { + out->type = type; + out->nreturn = count; + return 0; + } + } + + if (out) + { + LOG("%s: could not find function index %ju\n", __func__, + (uintmax_t)idx); + return -1; + } + + return 0; +} + +int section_function_check(const struct section *const s, + struct nw_mod *const m, const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.function) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (run(f, 0, NULL)) + { + LOG("%s: run failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + m->sections.function = start; + return 0; +} + +int section_function(const struct section *const s, + const struct nw_mod *const m, const varuint32 idx, + struct section_function *const f) +{ + const long offset = m->sections.function; + + if (offset <= 0) + { + LOG("%s: function section not found", __func__); + return -1; + } + else if (fseek(s->f, offset, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return run(s->f, idx, f); +} diff --git a/src/section/global.c b/src/section/global.c new file mode 100644 index 0000000..eff2e0b --- /dev/null +++ b/src/section/global.c @@ -0,0 +1,346 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +struct global_type +{ + bool mutable; + enum value_type type; + + union global_value + { + int32_t i32; + int64_t i64; + float f32; + double f64; + } value; +}; + +static int get_i32(const struct nw_interp *const i, + union global_value *const value) +{ + if (i->stack_i != sizeof value->i32) + { + LOG("%s: expected %zu used stack bytes, got %zu\n", + __func__, i->stack_i, sizeof value->i32); + return -1; + } + + value->i32 = *(const int32_t *)i->cfg.stack.buf; + return 0; +} + +static int get_i64(const struct nw_interp *const i, + union global_value *const value) +{ + if (i->stack_i != sizeof value->i64) + { + LOG("%s: expected %zu used stack bytes, got %zu\n", + __func__, i->stack_i, sizeof value->i64); + return -1; + } + + value->i64 = *(const int64_t *)i->cfg.stack.buf; + return 0; +} + +static int get_f32(const struct nw_interp *const i, + union global_value *const value) +{ + if (i->stack_i != sizeof value->f32) + { + LOG("%s: expected %zu used stack bytes, got %zu\n", + __func__, i->stack_i, sizeof value->f32); + return -1; + } + + value->f32 = *(const float *)i->cfg.stack.buf; + return 0; +} + +static int get_f64(const struct nw_interp *const i, + union global_value *const value) +{ + if (i->stack_i != sizeof value->f64) + { + LOG("%s: expected %zu used stack bytes, got %zu\n", + __func__, i->stack_i, sizeof value->f64); + return -1; + } + + value->f64 = *(const double *)i->cfg.stack.buf; + return 0; +} + +static int get_value(const struct nw_interp *const i, + const enum value_type type, union global_value *const value) +{ + static int (*const get[])(const struct nw_interp *, union global_value *) = + { + [VALUE_TYPE_I32] = get_i32, + [VALUE_TYPE_I64] = get_i64, + [VALUE_TYPE_F32] = get_f32, + [VALUE_TYPE_F64] = get_f64 + }; + + if (get[type](i, value)) + { + LOG("%s: get failed with type %d\n", __func__, type); + return -1; + } + + return 0; +} + +static int check_global_type(FILE *const f, struct global_type *const g) +{ + const struct nw_interp_cfg cfg = + { + .stack = NW_BUF(sizeof (union global_value)) + }; + + struct nw_interp i; + enum value_type vtype; + varint7 content_type; + varuint1 mutability; + union global_value value; + + if (varint7_read(f, &content_type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (get_value_type(content_type, &vtype)) + { + LOG("%s: get_value_type failed\n", __func__); + return -1; + } + else if (varuint1_read(f, &mutability)) + { + LOG("%s: varuint1_read failed\n", __func__); + return -1; + } + else if (interp_start(&cfg, f, &i)) + { + LOG("%s: interp_start failed\n", __func__); + return -1; + } + else if (interp_run_limited(&i, &interp_initexpr_set)) + { + LOG("%s: interp_run failed\n", __func__); + return -1; + } + else if (get_value(&i, vtype, &value)) + { + LOG("%s: get_value failed\n", __func__); + return -1; + } + + *g = (const struct global_type) + { + .mutable = mutability, + .type = vtype, + .value = value + }; + + return 0; +} + +static int set_i32(struct nw_interp *const i, void *const dst, + const struct global_type *const g) +{ + *(int32_t *)dst = g->value.i32; + return 0; +} + +static int set_i64(struct nw_interp *const i, void *const dst, + const struct global_type *const g) +{ + *(int64_t *)dst = g->value.i64; + return 0; +} + +static int set_f32(struct nw_interp *const i, void *const dst, + const struct global_type *const g) +{ + *(float *)dst = g->value.f32; + return 0; +} + +static int set_f64(struct nw_interp *const i, void *const dst, + const struct global_type *const g) +{ + *(double *)dst = g->value.f64; + return 0; +} + +static int add_global(struct nw_interp *const i, + const struct global_type *const g) +{ + const struct nw_gframe f = + { + .mutable = g->mutable, + .type = g->type + }; + + const size_t sz = sizeof f + get_type_size(g->type), + max = i->cfg.global.n; + + if (max < sz || i->global_i > max - sz) + { + LOG("%s: global memory overflow\n", __func__); + i->exception = "global memory overflow"; + return -1; + } + + void *const buf = (char *)i->cfg.global.buf + i->global_i; + struct nw_gframe *const dst = buf; + + if (i->gfp) + i->gfp->next = dst; + + *dst = f; + + static int (*const set[])(struct nw_interp *, void *, + const struct global_type *) = + { + [VALUE_TYPE_I32] = set_i32, + [VALUE_TYPE_I64] = set_i64, + [VALUE_TYPE_F32] = set_f32, + [VALUE_TYPE_F64] = set_f64 + }; + + if (set[g->type](i, dst + 1, g)) + { + LOG("%s: set failed with type %d\n", __func__, g->type); + return -1; + } + + i->global_i += sz; + i->gfp = dst; + return 0; +} + +static int check_global_types(FILE *const f, + struct nw_interp *const in) +{ + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + { + struct global_type g; + + if (check_global_type(f, &g)) + { + LOG("%s: check_global_type\n", __func__); + return -1; + } + else if (in && add_global(in, &g)) + { + LOG("%s: add_global failed\n", __func__); + return -1; + } + } + + return 0; +} + +static int check(FILE *const f, const unsigned long len) +{ + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check_global_types(f, NULL)) + { + LOG("%s: check_global_types failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_global_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.global) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check(f, len)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.global = offset; + return 0; +} + +int section_global_push(FILE *const f, const struct nw_mod *const m, + struct nw_interp *const i) +{ + if (fseek(f, m->sections.global, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check_global_types(f, i)) + { + LOG("%s: check_global_types failed\n", __func__); + return -1; + } + + return 0; +} diff --git a/src/section/import.c b/src/section/import.c new file mode 100644 index 0000000..5dd708e --- /dev/null +++ b/src/section/import.c @@ -0,0 +1,274 @@ +/* + * 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 <nw/sections.h> +#include <nw/fstring.h> +#include <nw/log.h> +#include <nw/types.h> +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +static int dump_import_name(FILE *const f, const varuint32 len) +{ + for (varuint32 i = 0; i < len; i++) + { + uint8_t byte; + + if (!fread(&byte, sizeof byte, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + + LOG("%c", (char)byte); + } + + return 0; +} + +static int check_module_string(const struct nw_mod_cfg *const cfg, + FILE *const f) +{ + varuint32 len; + + if (varuint32_read(f, &len)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (size_t i = 0; i < cfg->n_imports; i++) + { + const struct nw_import *const im = &cfg->imports[i]; + const char *const mod = im->module; + + if (strlen(mod) == len) + { + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (!fstrcmp(mod, f, true)) + return 0; + else if (fseek(f, offset, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + } + } + + LOG("%s: required imported module ", __func__); + dump_import_name(f, len); + LOG(" not found\n"); + return -1; +} + +static int check_field_string(const struct nw_mod_cfg *const cfg, + FILE *const f) +{ + varuint32 len; + + if (varuint32_read(f, &len)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (size_t i = 0; i < cfg->n_imports; i++) + { + const struct nw_import *const im = &cfg->imports[i]; + const char *const field = im->field; + + if (strlen(field) == len) + { + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (!fstrcmp(field, f, true)) + return 0; + else if (fseek(f, offset, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + } + } + + LOG("%s: required imported field ", __func__); + dump_import_name(f, len); + LOG(" not found\n"); + return -1; +} + +static int check_function_kind(FILE *const f) +{ + varuint32 type; + + if (varuint32_read(f, &type)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_table_kind(FILE *const f) +{ + varint7 type; + + if (varint7_read(f, &type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_memory_kind(FILE *const f) +{ + /* TODO. */ + return -1; +} + +static int check_global_kind(FILE *const f) +{ + /* TODO. */ + return -1; +} + +static int check_import_entry(const struct nw_mod_cfg *const cfg, + FILE *const f) +{ + if (check_module_string(cfg, f)) + { + LOG("%s: check_module_string failed\n", __func__); + return -1; + } + else if (check_field_string(cfg, f)) + { + LOG("%s: check_field_string failed\n", __func__); + return -1; + } + + uint8_t kind; + + if (!fread(&kind, sizeof kind, 1, f)) + { + LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", + __func__, feof(f), ferror(f)); + return -1; + } + + static int (*const fn[])(FILE *) = + { + [NW_KIND_FUNCTION] = check_function_kind, + [NW_KIND_TABLE] = check_table_kind, + [NW_KIND_MEMORY] = check_memory_kind, + [NW_KIND_GLOBAL] = check_global_kind + }; + + if (kind >= sizeof fn / sizeof *fn) + { + LOG("%s: unexpected kind %#" PRIx8 "\n", __func__, kind); + return -1; + } + + return fn[kind](f); +} + +static int check(const struct nw_mod_cfg *const cfg, FILE *const f, + const unsigned long len) +{ + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (check_import_entry(cfg, f)) + { + LOG("%s: check_import_entry failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_import_check(const struct section *const s, struct nw_mod *m, + const unsigned long len) +{ + const struct nw_mod_cfg *const cfg = s->cfg; + FILE *const f = s->f; + + if (m->sections.import) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check(cfg, f, len)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.import = offset; + return 0; +} diff --git a/src/section/memory.c b/src/section/memory.c new file mode 100644 index 0000000..0de5b4c --- /dev/null +++ b/src/section/memory.c @@ -0,0 +1,102 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +static int check_memory_type(FILE *const f, struct resizable_limits *const r) +{ + return check_resizable_limits(f, r); +} + +static int check(FILE *const f, const unsigned long len, + struct resizable_limits *const r) +{ + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return 1; + } + else if (count > 1) + { + LOG("%s: only 1 memory allowed\n", __func__); + return 1; + } + + for (varuint32 i = 0; i < count; i++) + if (check_memory_type(f, r)) + { + LOG("%s: check_memory_type failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_memory_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.memory) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + struct resizable_limits r; + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check(f, len, &r)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->heap_len = r.sz; + m->sections.memory = offset; + return 0; +} diff --git a/src/section/start.c b/src/section/start.c new file mode 100644 index 0000000..dd44652 --- /dev/null +++ b/src/section/start.c @@ -0,0 +1,18 @@ +/* + * 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 <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> + +int section_start_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + return -1; +} diff --git a/src/section/table.c b/src/section/table.c new file mode 100644 index 0000000..15fc619 --- /dev/null +++ b/src/section/table.c @@ -0,0 +1,112 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +static int check_table_type(FILE *const f) +{ + enum {ANYFUNC = 0x70}; + varint7 elem_type; + + if (varint7_read(f, &elem_type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (elem_type != ANYFUNC) + { + LOG("%s: expected %x, got %x", __func__, ANYFUNC, + (int)elem_type); + return -1; + } + + /* TODO: what to do with this? */ + struct resizable_limits r; + + return check_resizable_limits(f, &r); +} + +static int check(FILE *const f, const unsigned long len) +{ + const long start = ftell(f); + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + varuint32 count; + + if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + if (check_table_type(f)) + { + LOG("%s: check_table_type failed\n", __func__); + return -1; + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_table_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.table) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (check(f, len)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.table = offset; + return 0; +} diff --git a/src/section/type.c b/src/section/type.c new file mode 100644 index 0000000..0bf7a06 --- /dev/null +++ b/src/section/type.c @@ -0,0 +1,180 @@ +/* + * 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 <nw/log.h> +#include <nw/sections.h> +#include <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +static int check_value_type(FILE *const f, enum value_type *const vtype) +{ + varint7 value_type; + + if (varint7_read(f, &value_type)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (vtype && get_value_type(value_type, vtype)) + { + LOG("%s: get_value_type failed\n", __func__); + return -1; + } + + return 0; +} + +static int check_func_type(FILE *const f, struct retval *const r) +{ + varint7 form; + varuint32 param_count; + + if (varint7_read(f, &form)) + { + LOG("%s: varint7_read failed\n", __func__); + return -1; + } + else if (varuint32_read(f, ¶m_count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < param_count; i++) + if (check_value_type(f, NULL)) + { + LOG("%s: check_value_type failed\n", __func__); + return -1; + } + + varuint1 return_count; + enum value_type vtype; + + if (varuint1_read(f, &return_count)) + { + LOG("%s: varuint1_read failed\n", __func__); + return -1; + } + else if (return_count && check_value_type(f, &vtype)) + { + LOG("%s: check_value_type 2 failed\n", __func__); + return -1; + } + + *r = (const struct retval) + { + .returns = return_count, + .type = vtype + }; + + return 0; +} + +static int run(FILE *const f, const unsigned long len, const varuint32 idx, + struct nw_frame *const fr) +{ + const long start = ftell(f); + varuint32 count; + + if (start < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (varuint32_read(f, &count)) + { + LOG("%s: varuint32_read failed\n", __func__); + return -1; + } + + for (varuint32 i = 0; i < count; i++) + { + struct retval r; + + if (check_func_type(f, &r)) + { + LOG("%s: check_func_type failed\n", __func__); + return -1; + } + else if (fr && i == idx) + { + fr->retval = r; + return 0; + } + } + + const long end = ftell(f); + + if (end < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + + const unsigned long size = end - start; + + if (size != len) + { + LOG("%s: size exceeded (%lu expected, got %lu)\n", + __func__, len, size); + return -1; + } + + return 0; +} + +int section_type_check(const struct section *const s, struct nw_mod *const m, + const unsigned long len) +{ + FILE *const f = s->f; + + if (m->sections.type) + { + LOG("%s: ignoring duplicate section\n", __func__); + return fseek(f, len, SEEK_CUR); + } + + const long offset = ftell(f); + + if (offset < 0) + { + LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); + return -1; + } + else if (run(f, len, 0, NULL)) + { + LOG("%s: check failed\n", __func__); + return -1; + } + + m->sections.type = offset; + return 0; +} + +int section_type_push(FILE *const f, const struct nw_mod *const m, + const varuint32 idx, struct nw_frame *const fr) +{ + const long offset = m->sections.type; + + if (offset <= 0) + { + LOG("%s: function section not found", __func__); + return -1; + } + else if (fseek(f, offset, SEEK_SET)) + { + LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return run(f, 0, idx, fr); +} diff --git a/src/start.c b/src/start.c new file mode 100644 index 0000000..c22bddf --- /dev/null +++ b/src/start.c @@ -0,0 +1,81 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/interp.h> +#include <nw/log.h> +#include <nw/search.h> +#include <nw/sections.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int nw_start(const struct nw_inst_cfg *const icfg, struct nw_inst *const i) +{ + int ret = -1; + const struct nw_mod *const m = icfg->interp.m; + const struct nw_mod_cfg *const mcfg = &m->cfg; + FILE *const f = fopen(mcfg->path, "rb"); + struct search_fn fn; + struct nw_frame fr = {0}; + + *i = (const struct nw_inst){0}; + + if (!f) + { + LOG("%s: fopen(3) %s: %s\n", __func__, mcfg->path, strerror(errno)); + goto end; + } + else if (m->sections.start) + { + /* TODO. */ + } + else if (interp_start(&icfg->interp, f, &i->interp)) + { + LOG("%s: interp_start failed\n", __func__); + goto end; + } + else if (search_exported_fn("_start", m, f, &fn)) + { + LOG("%s: search_exported_fn failed\n", __func__); + goto end; + } + else if (section_global_push(f, m, &i->interp)) + { + LOG("%s: section_global_push failed\n", __func__); + goto end; + } + else if (section_type_push(f, m, fn.index, &fr)) + { + LOG("%s: section_type_push failed\n", __func__); + goto end; + } + else if (section_code_push(f, fn.start, &fr)) + { + LOG("%s: section_code_push failed\n", __func__); + goto end; + } + else if (interp_push(&i->interp, &fr)) + { + LOG("%s: interp_push failed\n", __func__); + goto end; + } + + ret = 0; + +end: + + if (ret && f && fclose(f)) + { + LOG("%s: fclose(3) %s: %s\n", __func__, mcfg->path, strerror(errno)); + ret = -1; + } + + return ret; +} diff --git a/src/stop.c b/src/stop.c new file mode 100644 index 0000000..1d5d604 --- /dev/null +++ b/src/stop.c @@ -0,0 +1,27 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/log.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int nw_stop(struct nw_inst *const i) +{ + struct nw_interp *const in = &i->interp; + + if (in->f && fclose(in->f)) + { + LOG("%s: fclose(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return 0; +} diff --git a/src/types.c b/src/types.c new file mode 100644 index 0000000..88c883e --- /dev/null +++ b/src/types.c @@ -0,0 +1,89 @@ +/* + * 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 <nw/leb128.h> +#include <nw/types.h> +#include <stdio.h> + +int varuint1_read(FILE *const f, varuint1 *const out) +{ + unsigned long long value; + + if (leb128_read_unsigned(f, 1, &value)) + return -1; + + *out = value; + return 0; +} + +int varint7_read(FILE *const f, varint7 *const out) +{ + long long value; + + if (leb128_read_signed(f, 7, &value)) + return -1; + + *out = value; + return 0; +} + +int varuint7_read(FILE *const f, varuint7 *const out) +{ + unsigned long long value; + + if (leb128_read_unsigned(f, 7, &value)) + return -1; + + *out = value; + return 0; +} + +int varuint32_read(FILE *const f, varuint32 *out) +{ + unsigned long long value; + + if (leb128_read_unsigned(f, 32, &value)) + return -1; + + *out = value; + return 0; +} + +int varint32_read(FILE *const f, varint32 *const out) +{ + long long value; + + if (leb128_read_signed(f, 32, &value)) + return -1; + + *out = value; + return 0; +} + +int varuint64_read(FILE *const f, varuint64 *const out) +{ + unsigned long long value; + + if (leb128_read_unsigned(f, 64, &value)) + return -1; + + *out = value; + return 0; +} + +int varint64_read(FILE *const f, varint64 *const out) +{ + long long value; + + if (leb128_read_signed(f, 64, &value)) + return -1; + + *out = value; + return 0; +} diff --git a/src/unload.c b/src/unload.c new file mode 100644 index 0000000..5b5ef9b --- /dev/null +++ b/src/unload.c @@ -0,0 +1,19 @@ +/* + * 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 <nanowasm/nw.h> +#include <nw/types.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +void nw_unload(struct nw_mod *const m) +{ + free(m); +} |
