aboutsummaryrefslogtreecommitdiff
path: root/src/section/code.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/section/code.c')
-rw-r--r--src/section/code.c429
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;
+}