diff options
Diffstat (limited to 'src/section/code.c')
| -rw-r--r-- | src/section/code.c | 429 |
1 files changed, 429 insertions, 0 deletions
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; +} |
