diff options
Diffstat (limited to 'src')
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, §ion)) + { + 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, ¶m_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); +} |
