aboutsummaryrefslogtreecommitdiff
path: root/src/section
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
downloadnanowasm-sync-f25b015e5b668028c34974bbb22faa4105c26690.tar.gz
First commit
Diffstat (limited to 'src/section')
-rw-r--r--src/section/CMakeLists.txt22
-rw-r--r--src/section/code.c429
-rw-r--r--src/section/common.c150
-rw-r--r--src/section/custom.c30
-rw-r--r--src/section/data.c18
-rw-r--r--src/section/element.c17
-rw-r--r--src/section/export.c161
-rw-r--r--src/section/function.c127
-rw-r--r--src/section/global.c346
-rw-r--r--src/section/import.c274
-rw-r--r--src/section/memory.c102
-rw-r--r--src/section/start.c18
-rw-r--r--src/section/table.c112
-rw-r--r--src/section/type.c180
14 files changed, 1986 insertions, 0 deletions
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 *)&in;
+
+ 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, &param_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);
+}