aboutsummaryrefslogtreecommitdiff
path: root/src/section/global.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-11-26 22:43:30 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2024-04-21 01:51:24 +0200
commitf25b015e5b668028c34974bbb22faa4105c26690 (patch)
tree28f2b08c17b3585d06694ad74004d0617eadb785 /src/section/global.c
downloadnanowasm-sync-f25b015e5b668028c34974bbb22faa4105c26690.tar.gz
First commit
Diffstat (limited to 'src/section/global.c')
-rw-r--r--src/section/global.c346
1 files changed, 346 insertions, 0 deletions
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;
+}