aboutsummaryrefslogtreecommitdiff
path: root/src
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>2023-12-29 23:36:21 +0100
commit52c18e60929b64285d4bb333f87482d954c9b2e0 (patch)
tree836ab3a01d160f45cda4f5a28522fc0bf471011c /src
First commitfirst-step
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt10
-rw-r--r--src/interp.c189
-rw-r--r--src/leb128.c61
-rw-r--r--src/load.c204
-rw-r--r--src/op/CMakeLists.txt28
-rw-r--r--src/op/block.c29
-rw-r--r--src/op/br.c29
-rw-r--r--src/op/br_if.c29
-rw-r--r--src/op/br_table.c46
-rw-r--r--src/op/call.c28
-rw-r--r--src/op/call_indirect.c40
-rw-r--r--src/op/current_memory.c35
-rw-r--r--src/op/else.c12
-rw-r--r--src/op/end.c15
-rw-r--r--src/op/f32_const.c29
-rw-r--r--src/op/f64_const.c29
-rw-r--r--src/op/get_global.c29
-rw-r--r--src/op/get_local.c29
-rw-r--r--src/op/i32_const.c29
-rw-r--r--src/op/i32_load.c34
-rw-r--r--src/op/i32_store.c34
-rw-r--r--src/op/i32_sub.c16
-rw-r--r--src/op/i64_const.c29
-rw-r--r--src/op/if.c27
-rw-r--r--src/op/loop.c27
-rw-r--r--src/op/nop.c13
-rw-r--r--src/op/return.c13
-rw-r--r--src/op/set_global.c29
-rw-r--r--src/op/set_local.c29
-rw-r--r--src/op/tee_local.c29
-rw-r--r--src/op/unreachable.c17
-rw-r--r--src/run.c10
-rw-r--r--src/section/CMakeLists.txt15
-rw-r--r--src/section/code.c235
-rw-r--r--src/section/common.c35
-rw-r--r--src/section/custom.c17
-rw-r--r--src/section/data.c8
-rw-r--r--src/section/element.c8
-rw-r--r--src/section/export.c147
-rw-r--r--src/section/function.c80
-rw-r--r--src/section/global.c110
-rw-r--r--src/section/import.c188
-rw-r--r--src/section/memory.c81
-rw-r--r--src/section/start.c8
-rw-r--r--src/section/table.c96
-rw-r--r--src/section/type.c126
-rw-r--r--src/types.c80
-rw-r--r--src/unload.c13
48 files changed, 2454 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..88bb33d
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,10 @@
+target_sources(${PROJECT_NAME} PRIVATE
+ interp.c
+ leb128.c
+ load.c
+ run.c
+ types.c
+ unload.c
+)
+add_subdirectory(op)
+add_subdirectory(section)
diff --git a/src/interp.c b/src/interp.c
new file mode 100644
index 0000000..b266d4e
--- /dev/null
+++ b/src/interp.c
@@ -0,0 +1,189 @@
+#include <interp.h>
+#include <interp_private.h>
+#include <opcodes.h>
+#include <ops.h>
+#include <wasm_types.h>
+#include <errno.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 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_F64_CONST] = op_i64_const,
+ [OP_F32_CONST] = op_f32_const,
+ [OP_F64_CONST] = op_f64_const,
+ [OP_I32_SUB] = op_i32_sub
+};
+
+const char *interp_get_exception(const struct interp *const i)
+{
+ return i->exception;
+}
+
+int interp_run(struct interp *const i)
+{
+ uint8_t op;
+ FILE *const f = i->cfg.f;
+
+ if (!fread(&op, sizeof op, 1, f))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return 1;
+ }
+ else if (op >= sizeof ops / sizeof *ops)
+ {
+ fprintf(stderr, "%s: invalid opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+ else if (!ops[op])
+ {
+ fprintf(stderr, "%s: unsupported opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+
+ return ops[op](i);
+}
+
+static int run_opcode_limited(struct interp *const in,
+ const struct interp_set *const set)
+{
+ uint8_t op;
+ FILE *const f = in->cfg.f;
+
+ if (!fread(&op, sizeof op, 1, f))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+
+ for (size_t i = 0; i < set->n; i++)
+ if (op == set->opcodes[i])
+ return ops[op](in);
+
+ fprintf(stderr, "%s: unexpected opcode %#" PRIx8 "\n", __func__, op);
+ return -1;
+}
+
+int interp_run_limited(struct interp *const in,
+ const struct interp_set *const set)
+{
+ while (!in->exit)
+ if (run_opcode_limited(in, set))
+ {
+ fprintf(stderr, "%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_F64_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)
+ {
+ fprintf(stderr, "%s: invalid opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+ else if (!checks[op])
+ {
+ fprintf(stderr, "%s: unsupported opcode %#" PRIx8 "\n", __func__, op);
+ return 1;
+ }
+
+ return checks[op](f);
+}
+
+void interp_free(struct interp *const i)
+{
+ free(i);
+}
+
+struct interp *interp_alloc(const struct interp_cfg *const cfg)
+{
+ struct interp *const ret = malloc(sizeof *ret);
+
+ if (!ret)
+ {
+ fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
+ return NULL;
+ }
+
+ *ret = (const struct interp)
+ {
+ .cfg = *cfg
+ };
+
+ return ret;
+}
diff --git a/src/leb128.c b/src/leb128.c
new file mode 100644
index 0000000..14b7967
--- /dev/null
+++ b/src/leb128.c
@@ -0,0 +1,61 @@
+#include <leb128.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)
+ {
+ fprintf(stderr, "%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..3ae3bae
--- /dev/null
+++ b/src/load.c
@@ -0,0 +1,204 @@
+#include <wasmfs.h>
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int failed_read(FILE *const f)
+{
+ return fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+}
+
+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))
+ {
+ failed_read(f);
+ return -1;
+ }
+ else if (memcmp(magic, m, sizeof magic))
+ {
+ fprintf(stderr, "%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))
+ {
+ failed_read(f);
+ return -1;
+ }
+ else if (memcmp(version, v, sizeof version))
+ {
+ fprintf(stderr, "%s: wrong version\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int load_section(struct wasmfs *const w, FILE *const f)
+{
+ enum
+ {
+ CUSTOM,
+ TYPE,
+ IMPORT,
+ FUNCTION,
+ TABLE,
+ MEMORY,
+ GLOBAL,
+ EXPORT,
+ START,
+ ELEMENT,
+ CODE,
+ DATA
+ };
+
+ static int (*const fn[])(struct wasmfs *, FILE *, unsigned long) =
+ {
+ [CUSTOM] = section_custom,
+ [TYPE] = section_type,
+ [IMPORT] = section_import,
+ [FUNCTION] = section_function,
+ [TABLE] = section_table,
+ [MEMORY] = section_memory,
+ [GLOBAL] = section_global,
+ [EXPORT] = section_export,
+ [START] = section_start,
+ [ELEMENT] = section_element,
+ [CODE] = section_code,
+ [DATA] = section_data
+ };
+
+ varuint7 section;
+ varuint32 len;
+ int ret;
+
+ if (varuint7_read(f, &section))
+ {
+ if (feof(f))
+ return 0;
+
+ fprintf(stderr, "%s: varuint7_read failed\n", __func__);
+ return 1;
+ }
+ else if (section >= sizeof fn / sizeof *fn)
+ {
+ fprintf(stderr, "%s: invalid section %u\n", __func__,
+ (unsigned)section);
+ return 1;
+ }
+ else if (varuint32_read(f, &len))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if ((ret = fn[section](w, f, len)))
+ fprintf(stderr, "%s: failed to load section %u\n", __func__,
+ (unsigned)section);
+
+ return ret;
+}
+
+static int load_sections(struct wasmfs *const w, FILE *const f)
+{
+ while (!feof(f))
+ if (load_section(w, f))
+ {
+ fprintf(stderr, "%s: load_section failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int load(struct wasmfs *const w, FILE *const f)
+{
+ if (check_magic(f))
+ {
+ fprintf(stderr, "%s: load_magic failed\n", __func__);
+ return -1;
+ }
+ else if (check_version(f))
+ {
+ fprintf(stderr, "%s: check_version failed\n", __func__);
+ return -1;
+ }
+ else if (load_sections(w, f))
+ {
+ fprintf(stderr, "%s: load_sections failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct wasmfs *wasmfs_load(const char *const path)
+{
+ struct wasmfs *ret = NULL, *w = NULL;
+ char *pathdup = NULL;
+ FILE *const f = fopen(path, "rb");
+ const size_t sz = strlen(path) + 1;
+
+ if (!f)
+ {
+ fprintf(stderr, "%s: fopen(3) %s: %s\n", __func__, path,
+ strerror(errno));
+ goto end;
+ }
+ else if (!(w = malloc(sizeof *w)))
+ {
+ fprintf(stderr, "%s: malloc(3) w: %s\n", __func__, strerror(errno));
+ goto end;
+ }
+ else if (!(pathdup = malloc(sz)))
+ {
+ fprintf(stderr, "%s: malloc(3) pathdup: %s\n", __func__,
+ strerror(errno));
+ goto end;
+ }
+
+ memcpy(pathdup, path, sz);
+
+ *w = (const struct wasmfs)
+ {
+ .path = pathdup
+ };
+
+ if (load(w, f))
+ {
+ fprintf(stderr, "%s: load failed\n", __func__);
+ goto end;
+ }
+
+ ret = w;
+
+end:
+ if (f && fclose(f))
+ {
+ fprintf(stderr, "%s: fclose(3) %s: %s\n", __func__, path,
+ strerror(errno));
+ ret = NULL;
+ }
+
+ if (!ret)
+ wasmfs_unload(w);
+
+ return ret;
+}
diff --git a/src/op/CMakeLists.txt b/src/op/CMakeLists.txt
new file mode 100644
index 0000000..a97b212
--- /dev/null
+++ b/src/op/CMakeLists.txt
@@ -0,0 +1,28 @@
+target_sources(${PROJECT_NAME} PRIVATE
+ unreachable.c
+ nop.c
+ block.c
+ loop.c
+ if.c
+ else.c
+ end.c
+ br.c
+ br_if.c
+ br_table.c
+ return.c
+ call.c
+ call_indirect.c
+ get_local.c
+ set_local.c
+ tee_local.c
+ get_global.c
+ set_global.c
+ i32_load.c
+ i32_store.c
+ current_memory.c
+ i32_const.c
+ i64_const.c
+ f32_const.c
+ f64_const.c
+ i32_sub.c
+)
diff --git a/src/op/block.c b/src/op/block.c
new file mode 100644
index 0000000..c97879c
--- /dev/null
+++ b/src/op/block.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_block(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_block(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/br.c b/src/op/br.c
new file mode 100644
index 0000000..ef5c34f
--- /dev/null
+++ b/src/op/br.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_br(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/br_if.c b/src/op/br_if.c
new file mode 100644
index 0000000..1711ada
--- /dev/null
+++ b/src/op/br_if.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 relative_depth;
+
+ if (varuint32_read(f, &relative_depth))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_br_if(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_br_if(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/br_table.c b/src/op/br_table.c
new file mode 100644
index 0000000..09ae8e4
--- /dev/null
+++ b/src/op/br_table.c
@@ -0,0 +1,46 @@
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 target_count, default_target;
+
+ if (varuint32_read(f, &target_count))
+ {
+ fprintf(stderr, "%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))
+ {
+ fprintf(stderr, "%s: varuint32_read target_table failed\n",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (varuint32_read(f, &default_target))
+ {
+ fprintf(stderr, "%s: varuint32_read default_target failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_br_table(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_br_table(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/call.c b/src/op/call.c
new file mode 100644
index 0000000..62ff5c9
--- /dev/null
+++ b/src/op/call.c
@@ -0,0 +1,28 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 function_index;
+
+ if (varuint32_read(f, &function_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_call(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_call(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/call_indirect.c b/src/op/call_indirect.c
new file mode 100644
index 0000000..9cb831c
--- /dev/null
+++ b/src/op/call_indirect.c
@@ -0,0 +1,40 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 type_index;
+ varuint1 reserved;
+
+ if (varuint32_read(f, &type_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+ else if (varuint1_read(f, &reserved))
+ {
+ fprintf(stderr, "%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ fprintf(stderr, "%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_call_indirect(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_call_indirect(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/current_memory.c b/src/op/current_memory.c
new file mode 100644
index 0000000..4c1f26a
--- /dev/null
+++ b/src/op/current_memory.c
@@ -0,0 +1,35 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint1 reserved;
+
+ if (varuint1_read(f, &reserved))
+ {
+ fprintf(stderr, "%s: varuint1_read failed\n", __func__);
+ return 1;
+ }
+ else if (reserved)
+ {
+ fprintf(stderr, "%s: unexpected non-zero reserved value %u\n",
+ __func__, (unsigned)reserved);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_current_memory(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_current_memory(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/else.c b/src/op/else.c
new file mode 100644
index 0000000..4e4dcb6
--- /dev/null
+++ b/src/op/else.c
@@ -0,0 +1,12 @@
+#include <ops.h>
+#include <interp.h>
+
+int op_else(struct interp *const i)
+{
+ return -1;
+}
+
+int check_else(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/end.c b/src/op/end.c
new file mode 100644
index 0000000..93cdae7
--- /dev/null
+++ b/src/op/end.c
@@ -0,0 +1,15 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <stdbool.h>
+
+int op_end(struct interp *const i)
+{
+ i->exit = true;
+ return 0;
+}
+
+int check_end(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/f32_const.c b/src/op/f32_const.c
new file mode 100644
index 0000000..e42f19f
--- /dev/null
+++ b/src/op/f32_const.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 value;
+
+ if (varuint32_read(f, &value))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f32_const(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_f32_const(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/f64_const.c b/src/op/f64_const.c
new file mode 100644
index 0000000..01c3401
--- /dev/null
+++ b/src/op/f64_const.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint64 value;
+
+ if (varuint64_read(f, &value))
+ {
+ fprintf(stderr, "%s: varuint64_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_f64_const(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_f64_const(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/get_global.c b/src/op/get_global.c
new file mode 100644
index 0000000..6366c4e
--- /dev/null
+++ b/src/op/get_global.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_get_global(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_get_global(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/get_local.c b/src/op/get_local.c
new file mode 100644
index 0000000..f1de025
--- /dev/null
+++ b/src/op/get_local.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_get_local(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_get_local(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/i32_const.c b/src/op/i32_const.c
new file mode 100644
index 0000000..722dab9
--- /dev/null
+++ b/src/op/i32_const.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint32 value;
+
+ if (varint32_read(f, &value))
+ {
+ fprintf(stderr, "%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i32_const(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_i32_const(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/i32_load.c b/src/op/i32_load.c
new file mode 100644
index 0000000..e00cc2d
--- /dev/null
+++ b/src/op/i32_load.c
@@ -0,0 +1,34 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint32 flags, offset;
+
+ if (varint32_read(f, &flags))
+ {
+ fprintf(stderr, "%s: varint32_read flags failed\n", __func__);
+ return 1;
+ }
+ else if (varint32_read(f, &offset))
+ {
+ fprintf(stderr, "%s: varint32_read offset failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_i32_load(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_i32_load(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/i32_store.c b/src/op/i32_store.c
new file mode 100644
index 0000000..207a394
--- /dev/null
+++ b/src/op/i32_store.c
@@ -0,0 +1,34 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint32 flags, offset;
+
+ if (varint32_read(f, &flags))
+ {
+ fprintf(stderr, "%s: varint32_read flags failed\n", __func__);
+ return 1;
+ }
+ else if (varint32_read(f, &offset))
+ {
+ fprintf(stderr, "%s: varint32_read offset failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_i32_store(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_i32_store(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/i32_sub.c b/src/op/i32_sub.c
new file mode 100644
index 0000000..24a0834
--- /dev/null
+++ b/src/op/i32_sub.c
@@ -0,0 +1,16 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+int op_i32_sub(struct interp *const i)
+{
+ return -1;
+}
+
+int check_i32_sub(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/i64_const.c b/src/op/i64_const.c
new file mode 100644
index 0000000..c2f1592
--- /dev/null
+++ b/src/op/i64_const.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint64 value;
+
+ if (varint64_read(f, &value))
+ {
+ fprintf(stderr, "%s: varint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_i64_const(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_i64_const(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/if.c b/src/op/if.c
new file mode 100644
index 0000000..526c8f5
--- /dev/null
+++ b/src/op/if.c
@@ -0,0 +1,27 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_if(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_if(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/loop.c b/src/op/loop.c
new file mode 100644
index 0000000..b7ea2b3
--- /dev/null
+++ b/src/op/loop.c
@@ -0,0 +1,27 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varint7 sig;
+
+ if (varint7_read(f, &sig))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+int op_loop(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_loop(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/nop.c b/src/op/nop.c
new file mode 100644
index 0000000..89bd895
--- /dev/null
+++ b/src/op/nop.c
@@ -0,0 +1,13 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+
+int op_nop(struct interp *const i)
+{
+ return 0;
+}
+
+int check_nop(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/return.c b/src/op/return.c
new file mode 100644
index 0000000..84ac408
--- /dev/null
+++ b/src/op/return.c
@@ -0,0 +1,13 @@
+#include <ops.h>
+#include <interp.h>
+#include <stdio.h>
+
+int op_return(struct interp *const i)
+{
+ return -1;
+}
+
+int check_return(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/op/set_global.c b/src/op/set_global.c
new file mode 100644
index 0000000..9443323
--- /dev/null
+++ b/src/op/set_global.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 global_index;
+
+ if (varuint32_read(f, &global_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_set_global(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_set_global(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/set_local.c b/src/op/set_local.c
new file mode 100644
index 0000000..8424713
--- /dev/null
+++ b/src/op/set_local.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_set_local(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_set_local(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/tee_local.c b/src/op/tee_local.c
new file mode 100644
index 0000000..8b11fd8
--- /dev/null
+++ b/src/op/tee_local.c
@@ -0,0 +1,29 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <wasm_types.h>
+#include <stddef.h>
+#include <stdio.h>
+
+static int op(FILE *const f, struct interp *const i)
+{
+ varuint32 local_index;
+
+ if (varuint32_read(f, &local_index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int op_tee_local(struct interp *const i)
+{
+ return op(i->cfg.f, i);
+}
+
+int check_tee_local(FILE *const f)
+{
+ return op(f, NULL);
+}
diff --git a/src/op/unreachable.c b/src/op/unreachable.c
new file mode 100644
index 0000000..c343052
--- /dev/null
+++ b/src/op/unreachable.c
@@ -0,0 +1,17 @@
+#include <ops.h>
+#include <interp.h>
+#include <interp_private.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+int op_unreachable(struct interp *const i)
+{
+ i->exception = "Unreachable instruction";
+ i->exit = true;
+ return 1;
+}
+
+int check_unreachable(FILE *const f)
+{
+ return 0;
+}
diff --git a/src/run.c b/src/run.c
new file mode 100644
index 0000000..07a0fb8
--- /dev/null
+++ b/src/run.c
@@ -0,0 +1,10 @@
+#include <wasmfs.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+int wasmfs_run(struct wasmfs_inst *const i)
+{
+ return -1;
+}
diff --git a/src/section/CMakeLists.txt b/src/section/CMakeLists.txt
new file mode 100644
index 0000000..62d9cef
--- /dev/null
+++ b/src/section/CMakeLists.txt
@@ -0,0 +1,15 @@
+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..6e131e7
--- /dev/null
+++ b/src/section/code.c
@@ -0,0 +1,235 @@
+#include <opcodes.h>
+#include <interp.h>
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <inttypes.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)
+ {
+ fprintf(stderr, "%s: ftell(3) before: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+ else if (!fread(&byte, sizeof byte, 1, f))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ else if (rem == 1 && byte != OP_END)
+ {
+ fprintf(stderr, "%s: unexpected opcode %#" PRIx8 " at body end\n",
+ __func__, byte);
+ return -1;
+ }
+ else if (interp_check_opcode(byte, f))
+ {
+ fprintf(stderr, "%s: interp_check_opcode failed\n", __func__);
+ return -1;
+ }
+
+ const long after = ftell(f);
+
+ if (after < 0)
+ {
+ fprintf(stderr, "%s: ftell(3) after: %s\n", __func__,
+ strerror(errno));
+ return -1;
+ }
+
+ rem -= after - before;
+ }
+
+ return 0;
+}
+
+static int check_type(FILE *const f)
+{
+ varint7 type;
+
+ if (varint7_read(f, &type))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_local(FILE *const f)
+{
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ else if (check_type(f))
+ {
+ fprintf(stderr, "%s: check_type failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_locals(FILE *const f)
+{
+ varuint32 local_count;
+
+ if (varuint32_read(f, &local_count))
+ {
+ fprintf(stderr, "%s: varuint32_read local_count failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < local_count; i++)
+ if (check_local(f))
+ {
+ fprintf(stderr, "%s: check_local failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_function_body(FILE *const f)
+{
+ varuint32 body_size;
+
+ if (varuint32_read(f, &body_size))
+ {
+ fprintf(stderr, "%s: varuint32_read body_size failed\n", __func__);
+ return -1;
+ }
+
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3) start: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_locals(f))
+ {
+ fprintf(stderr, "%s: check_locals failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3) end: %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long consumed = end - start;
+
+ if (consumed > body_size)
+ {
+ fprintf(stderr, "%s: exceeded function body size\n", __func__);
+ return -1;
+ }
+ else if (check_body(f, body_size - consumed))
+ {
+ fprintf(stderr, "%s: check_body failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_function_bodies(FILE *const f)
+{
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_function_body(f))
+ {
+ fprintf(stderr, "%s: check_function_body 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)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_function_bodies(f))
+ {
+ fprintf(stderr, "%s: check_function_bodies failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_code(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.code)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.code = offset;
+ return 0;
+}
diff --git a/src/section/common.c b/src/section/common.c
new file mode 100644
index 0000000..58ddddb
--- /dev/null
+++ b/src/section/common.c
@@ -0,0 +1,35 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int check_resizable_limits(FILE *const f)
+{
+ varuint1 flags;
+ varuint32 initial;
+
+ if (varuint1_read(f, &flags))
+ {
+ fprintf(stderr, "%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &initial))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ if (flags)
+ {
+ varuint32 maximum;
+
+ if (varuint32_read(f, &maximum))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/section/custom.c b/src/section/custom.c
new file mode 100644
index 0000000..9544f17
--- /dev/null
+++ b/src/section/custom.c
@@ -0,0 +1,17 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+int section_custom(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (fseek(f, len, SEEK_CUR))
+ {
+ fprintf(stderr, "%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..5a04d7b
--- /dev/null
+++ b/src/section/data.c
@@ -0,0 +1,8 @@
+#include <sections.h>
+#include <wasm_types.h>
+
+int section_data(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/element.c b/src/section/element.c
new file mode 100644
index 0000000..370a952
--- /dev/null
+++ b/src/section/element.c
@@ -0,0 +1,8 @@
+#include <sections.h>
+#include <wasm_types.h>
+
+int section_element(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/export.c b/src/section/export.c
new file mode 100644
index 0000000..9ca8de5
--- /dev/null
+++ b/src/section/export.c
@@ -0,0 +1,147 @@
+#include <sections.h>
+#include <wasm_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))
+ {
+ fprintf(stderr, "%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))
+ {
+ fprintf(stderr, "%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)
+{
+ varuint32 type;
+
+ if (varuint32_read(f, &type))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_index(FILE *const f)
+{
+ varuint32 index;
+
+ if (varuint32_read(f, &index))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_export_entry(FILE *const f)
+{
+ if (check_string(f))
+ {
+ fprintf(stderr, "%s: check_string failed\n", __func__);
+ return -1;
+ }
+ else if (check_kind(f))
+ {
+ fprintf(stderr, "%s: check_kind failed\n", __func__);
+ return -1;
+ }
+ else if (check_index(f))
+ {
+ fprintf(stderr, "%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)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_export_entry(f))
+ {
+ fprintf(stderr, "%s: check_export_entry failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_export(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.export)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.export = offset;
+ return 0;
+}
diff --git a/src/section/function.c b/src/section/function.c
new file mode 100644
index 0000000..f03971c
--- /dev/null
+++ b/src/section/function.c
@@ -0,0 +1,80 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read count failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ {
+ varuint32 type;
+
+ if (varuint32_read(f, &type))
+ {
+ fprintf(stderr, "%s: varuint32_read type failed\n", __func__);
+ return -1;
+ }
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_function(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.function)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.function = offset;
+ return 0;
+}
diff --git a/src/section/global.c b/src/section/global.c
new file mode 100644
index 0000000..5616e21
--- /dev/null
+++ b/src/section/global.c
@@ -0,0 +1,110 @@
+#include <sections.h>
+#include <interp.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+struct global_type
+{
+ varint7 value_type;
+ varuint1 mutability;
+};
+
+static int check_global_type(FILE *const f, struct global_type *const g)
+{
+ if (varint7_read(f, &g->value_type))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (varuint1_read(f, &g->mutability))
+ {
+ fprintf(stderr, "%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ int ret = -1;
+ struct global_type g;
+ struct interp *i = NULL;
+ const long start = ftell(f);
+ const struct interp_cfg cfg =
+ {
+ .f = f
+ };
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check_global_type(f, &g))
+ {
+ fprintf(stderr, "%s: check_global_type\n", __func__);
+ goto end;
+ }
+ else if (!(i = interp_alloc(&cfg)))
+ {
+ fprintf(stderr, "%s: interp_alloc failed\n", __func__);
+ goto end;
+ }
+ else if (interp_run_limited(i, &interp_initexpr_set))
+ {
+ fprintf(stderr, "%s: interp_run failed\n", __func__);
+ goto end;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ ret = 0;
+
+end:
+ interp_free(i);
+ return ret;
+}
+
+int section_global(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.global)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.global = offset;
+ return 0;
+}
diff --git a/src/section/import.c b/src/section/import.c
new file mode 100644
index 0000000..06df4e1
--- /dev/null
+++ b/src/section/import.c
@@ -0,0 +1,188 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_string(FILE *const f)
+{
+ varuint32 len;
+
+ if (varuint32_read(f, &len))
+ {
+ fprintf(stderr, "%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))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int check_function_kind(FILE *const f)
+{
+ varuint32 type;
+
+ if (varuint32_read(f, &type))
+ {
+ fprintf(stderr, "%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))
+ {
+ fprintf(stderr, "%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(FILE *const f)
+{
+ if (check_string(f))
+ {
+ fprintf(stderr, "%s: check_string module failed\n", __func__);
+ return -1;
+ }
+ else if (check_string(f))
+ {
+ fprintf(stderr, "%s: check_string field failed\n", __func__);
+ return -1;
+ }
+
+ uint8_t kind;
+
+ enum
+ {
+ FUNCTION,
+ TABLE,
+ MEMORY,
+ GLOBAL
+ };
+
+ if (!fread(&kind, sizeof kind, 1, f))
+ {
+ fprintf(stderr, "%s: fread(3) failed, feof=%d, ferror=%d\n",
+ __func__, feof(f), ferror(f));
+ return -1;
+ }
+
+ static int (*const fn[])(FILE *) =
+ {
+ [FUNCTION] = check_function_kind,
+ [TABLE] = check_table_kind,
+ [MEMORY] = check_memory_kind,
+ [GLOBAL] = check_global_kind
+ };
+
+ if (kind >= sizeof fn / sizeof *fn)
+ {
+ fprintf(stderr, "%s: unexpected kind %#" PRIx8 "\n", __func__, kind);
+ return -1;
+ }
+
+ return fn[kind](f);
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_import_entry(f))
+ {
+ fprintf(stderr, "%s: check_import_entry failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_import(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.import)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.import = offset;
+ return 0;
+}
diff --git a/src/section/memory.c b/src/section/memory.c
new file mode 100644
index 0000000..aba461a
--- /dev/null
+++ b/src/section/memory.c
@@ -0,0 +1,81 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_memory_type(FILE *const f)
+{
+ return check_resizable_limits(f);
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_memory_type(f))
+ {
+ fprintf(stderr, "%s: check_memory_type failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_memory(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.memory)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.memory = offset;
+ return 0;
+}
diff --git a/src/section/start.c b/src/section/start.c
new file mode 100644
index 0000000..821525d
--- /dev/null
+++ b/src/section/start.c
@@ -0,0 +1,8 @@
+#include <sections.h>
+#include <wasm_types.h>
+
+int section_start(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ return -1;
+}
diff --git a/src/section/table.c b/src/section/table.c
new file mode 100644
index 0000000..9f793c7
--- /dev/null
+++ b/src/section/table.c
@@ -0,0 +1,96 @@
+#include <sections.h>
+#include <wasm_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))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (elem_type != ANYFUNC)
+ {
+ fprintf(stderr, "%s: expected %x, got %x", __func__, ANYFUNC,
+ (int)elem_type);
+ return -1;
+ }
+
+ return check_resizable_limits(f);
+}
+
+static int check(FILE *const f, const unsigned long len)
+{
+ const long start = ftell(f);
+
+ if (start < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ varuint32 count;
+
+ if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_table_type(f))
+ {
+ fprintf(stderr, "%s: check_table_type failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_table(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.table)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.table = offset;
+ return 0;
+}
diff --git a/src/section/type.c b/src/section/type.c
new file mode 100644
index 0000000..fad2208
--- /dev/null
+++ b/src/section/type.c
@@ -0,0 +1,126 @@
+#include <sections.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+static int check_value_type(FILE *const f)
+{
+ varint7 value_type;
+
+ if (varint7_read(f, &value_type))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_func_type(FILE *const f)
+{
+ varint7 form;
+ varuint32 param_count;
+
+ if (varint7_read(f, &form))
+ {
+ fprintf(stderr, "%s: varint7_read failed\n", __func__);
+ return -1;
+ }
+ else if (varuint32_read(f, &param_count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < param_count; i++)
+ if (check_value_type(f))
+ {
+ fprintf(stderr, "%s: check_value_type failed\n", __func__);
+ return -1;
+ }
+
+ varuint1 return_count;
+
+ if (varuint1_read(f, &return_count))
+ {
+ fprintf(stderr, "%s: varuint1_read failed\n", __func__);
+ return -1;
+ }
+ else if (return_count && check_value_type(f))
+ {
+ fprintf(stderr, "%s: check_value_type 2 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)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (varuint32_read(f, &count))
+ {
+ fprintf(stderr, "%s: varuint32_read failed\n", __func__);
+ return -1;
+ }
+
+ for (varuint32 i = 0; i < count; i++)
+ if (check_func_type(f))
+ {
+ fprintf(stderr, "%s: check_func_type failed\n", __func__);
+ return -1;
+ }
+
+ const long end = ftell(f);
+
+ if (end < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+
+ const unsigned long size = end - start;
+
+ if (size != len)
+ {
+ fprintf(stderr, "%s: size exceeded (%lu expected, got %lu)\n",
+ __func__, len, size);
+ return -1;
+ }
+
+ return 0;
+}
+
+int section_type(struct wasmfs *const w, FILE *const f,
+ const unsigned long len)
+{
+ if (w->sections.type)
+ {
+ fprintf(stderr, "%s: ignoring duplicate section\n", __func__);
+ return 0;
+ }
+
+ const long offset = ftell(f);
+
+ if (offset < 0)
+ {
+ fprintf(stderr, "%s: ftell(3): %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ else if (check(f, len))
+ {
+ fprintf(stderr, "%s: check failed\n", __func__);
+ return -1;
+ }
+
+ w->sections.type = offset;
+ return 0;
+}
diff --git a/src/types.c b/src/types.c
new file mode 100644
index 0000000..8c2a855
--- /dev/null
+++ b/src/types.c
@@ -0,0 +1,80 @@
+#include <leb128.h>
+#include <wasm_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..5304701
--- /dev/null
+++ b/src/unload.c
@@ -0,0 +1,13 @@
+#include <wasmfs.h>
+#include <wasm_types.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+void wasmfs_unload(struct wasmfs *const w)
+{
+ if (w)
+ free(w->path);
+
+ free(w);
+}