#include #include #include #include #include #include #include #include #include #include #include #include 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; }