/* * nanowasm, a tiny WebAssembly/Wasm interpreter * Copyright (C) 2023-2024 Xavier Del Campo Romero * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include static int run(FILE *const f, const varuint32 idx, struct nw_interp *const in) { return -1; } static int check_body(FILE *const f, const unsigned long n) { unsigned long rem = n; while (rem) { const long before = ftell(f); uint8_t byte; if (before < 0) { LOG("%s: ftell(3) before: %s\n", __func__, strerror(errno)); return -1; } else if (!fread(&byte, sizeof byte, 1, f)) { LOG("%s: fread(3) failed, feof=%d, ferror=%d\n", __func__, feof(f), ferror(f)); return -1; } else if (rem == 1 && byte != OP_END) { LOG("%s: unexpected opcode %#" PRIx8 " at body end\n", __func__, byte); return -1; } else if (interp_check_opcode(byte, f)) { LOG("%s: interp_check_opcode failed\n", __func__); return -1; } const long after = ftell(f); if (after < 0) { LOG("%s: ftell(3) after: %s\n", __func__, strerror(errno)); return -1; } rem -= after - before; } return 0; } static int push_i32(struct nw_interp *const i) { const int32_t value = 0; return interp_stack_push(i, &value, sizeof value); } static int push_i64(struct nw_interp *const i) { const int64_t value = 0; return interp_stack_push(i, &value, sizeof value); } static int push_f32(struct nw_interp *const i) { const float value = 0; return interp_stack_push(i, &value, sizeof value); } static int push_f64(struct nw_interp *const i) { const double value = 0; return interp_stack_push(i, &value, sizeof value); } static int push_locals(struct nw_interp *const in, const varuint32 count, const varint7 type) { enum value_type vtype; if (get_value_type(type, &vtype)) { LOG("%s: get_value_type failed\n", __func__); return -1; } static int (*const f[])(struct nw_interp *) = { [VALUE_TYPE_I32] = push_i32, [VALUE_TYPE_I64] = push_i64, [VALUE_TYPE_F32] = push_f32, [VALUE_TYPE_F64] = push_f64 }; for (varuint32 i = 0; i < count; i++) if (f[vtype](in)) { LOG("%s: local variable push failed, type=%s, index=%lu\n", __func__, value_type_tostr(vtype), (unsigned long)i); return -1; } return 0; } static int check_local_group(FILE *const f, struct nw_interp *const i) { varuint32 count; varint7 type; if (varuint32_read(f, &count)) { LOG("%s: varuint32_read failed\n", __func__); return -1; } else if (varint7_read(f, &type)) { LOG("%s: varint7_read failed\n", __func__); return -1; } else if (i && push_locals(i, count, type)) { LOG("%s: push_locals failed\n", __func__); return -1; } return 0; } #if 0 static int run(FILE *const f, struct nw_interp *const in) { varuint32 local_count; if (varuint32_read(f, &local_count)) { LOG("%s: varuint32_read local_count failed\n", __func__); return -1; } for (varuint32 i = 0; i < local_count; i++) if (check_local_group(f, in)) { LOG("%s: check_local failed\n", __func__); return -1; } return 0; } #endif static int push_frame(struct nw_interp *const i, const struct nw_frame *const src) { struct nw_frame *const dst = interp_stackptr(i); if (interp_stack_push(i, src, sizeof (*src))) { LOG("%s: interp_stack_push failed\n", __func__); return -1; } else if (i->fp) i->fp->next = dst; i->fp = dst; return 0; } static int check_function_body(FILE *const f, long *const out) { varuint32 body_size; if (varuint32_read(f, &body_size)) { LOG("%s: varuint32_read body_size failed\n", __func__); return -1; } const long start = ftell(f); if (start < 0) { LOG("%s: ftell(3) start: %s\n", __func__, strerror(errno)); return -1; } else if (run(f, 0, NULL)) { LOG("%s: check_local_groups failed\n", __func__); return -1; } const long end = ftell(f); if (end < 0) { LOG("%s: ftell(3) end: %s\n", __func__, strerror(errno)); return -1; } const unsigned long consumed = end - start; if (consumed > body_size) { LOG("%s: exceeded function body size\n", __func__); return -1; } else if (check_body(f, body_size - consumed)) { LOG("%s: check_body failed\n", __func__); return -1; } *out = start; return 0; } int section_code_check(const struct section *const s, struct nw_mod *const m, const unsigned long len) { FILE *const f = s->f; if (m->sections.code) { LOG("%s: ignoring duplicate section\n", __func__); return fseek(f, len, SEEK_CUR); } const long start = ftell(f); if (start < 0) { LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); return -1; } else if (run(f, 0, NULL)) { LOG("%s: run failed\n", __func__); return -1; } const long end = ftell(f); if (end < 0) { LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); return -1; } const unsigned long size = end - start; if (size != len) { LOG("%s: size exceeded (%lu expected, got %lu)\n", __func__, len, size); return -1; } m->sections.code = start; return 0; } #if 0 static int run(FILE *const f, const varuint32 idx, struct nw_interp *const in) { varuint32 count; if (varuint32_read(f, &count)) { LOG("%s: varuint32_read failed\n", __func__); return -1; } for (varuint32 i = 0; i < count; i++) { long start; if (check_function_body(f, &start)) { LOG("%s: check_function_body failed\n", __func__); return -1; } else if (out && i == idx) { out->start = start; return 0; } } return 0; } #endif int section_code(const struct section *const s, const struct nw_mod *const m, const varuint32 idx, struct section_code *const out) { const long offset = m->sections.code; if (offset <= 0) { LOG("%s: code section not found", __func__); return -1; } else if (fseek(s->f, offset, SEEK_SET)) { LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); return -1; } return run(s->f, idx, out); } int section_code_push(FILE *const f, struct nw_interp *const i) { #if 0 return run(f, i); #else return run(f, 0, i); #endif }