/* * 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 struct global_type { bool mutable; enum value_type type; union global_value { int32_t i32; int64_t i64; float f32; double f64; } value; }; static int get_global(struct nw_interp *const i, void *const dst, const size_t n) { if (i->stack_i != n) { LOG("%s: expected %zu used stack bytes, got %zu\n", __func__, i->stack_i, n); i->exception = "unexpected stack consumption"; return -1; } return interp_global_pop(i, dst, n); } static int get_i32(struct nw_interp *const i, union global_value *const value) { return get_global(i, &value->i32, sizeof value->i32); } static int get_i64(struct nw_interp *const i, union global_value *const value) { return get_global(i, &value->i64, sizeof value->i64); } static int get_f32(struct nw_interp *const i, union global_value *const value) { return get_global(i, &value->f32, sizeof value->f32); } static int get_f64(struct nw_interp *const i, union global_value *const value) { return get_global(i, &value->f64, sizeof value->f64); } static int get_value(struct nw_interp *const i, const enum value_type type, union global_value *const value) { static int (*const get[])(struct nw_interp *, union global_value *) = { [VALUE_TYPE_I32] = get_i32, [VALUE_TYPE_I64] = get_i64, [VALUE_TYPE_F32] = get_f32, [VALUE_TYPE_F64] = get_f64 }; if (get[type](i, value)) { LOG("%s: get failed with type %d\n", __func__, type); return -1; } return 0; } static int check_global_type(FILE *const f, struct global_type *const g) { const struct nw_interp_cfg cfg = { .stack = NW_BUF(sizeof (union global_value)) }; struct nw_interp i; enum value_type vtype; varint7 content_type; varuint1 mutability; union global_value value; if (varint7_read(f, &content_type)) { LOG("%s: varint7_read failed\n", __func__); return -1; } else if (get_value_type(content_type, &vtype)) { LOG("%s: get_value_type failed\n", __func__); return -1; } else if (varuint1_read(f, &mutability)) { LOG("%s: varuint1_read failed\n", __func__); return -1; } else if (interp_start(&cfg, f, &i)) { LOG("%s: interp_start failed\n", __func__); return -1; } else if (interp_run_limited(&i, &interp_initexpr_set)) { LOG("%s: interp_run failed\n", __func__); return -1; } else if (get_value(&i, vtype, &value)) { LOG("%s: get_value failed\n", __func__); return -1; } *g = (const struct global_type) { .mutable = mutability, .type = vtype, .value = value }; return 0; } static int set_i32(struct nw_interp *const i, void *const dst, const struct global_type *const g) { const int32_t value = g->value.i32; return interp_global_push(i, &value, sizeof value); } static int set_i64(struct nw_interp *const i, void *const dst, const struct global_type *const g) { const int64_t value = g->value.i64; return interp_global_push(i, &value, sizeof value); } static int set_f32(struct nw_interp *const i, void *const dst, const struct global_type *const g) { const float value = g->value.f32; return interp_global_push(i, &value, sizeof value); } static int set_f64(struct nw_interp *const i, void *const dst, const struct global_type *const g) { const double value = g->value.f64; return interp_global_push(i, &value, sizeof value); } static int add_global(struct nw_interp *const i, const struct global_type *const g) { const struct nw_gframe f = { .mutable = g->mutable, .type = g->type }; struct nw_gframe *const dst = interp_globalptr(i);; if (interp_global_push(i, &f, sizeof f)) { LOG("%s: interp_global_push failed\n", __func__); return -1; } static int (*const set[])(struct nw_interp *, void *, const struct global_type *) = { [VALUE_TYPE_I32] = set_i32, [VALUE_TYPE_I64] = set_i64, [VALUE_TYPE_F32] = set_f32, [VALUE_TYPE_F64] = set_f64 }; if (set[g->type](i, dst + 1, g)) { LOG("%s: set failed with type %s\n", __func__, value_type_tostr(g->type)); return -1; } if (i->gfp) i->gfp->next = dst; i->gfp = dst; return 0; } static int check_global_types(FILE *const f, struct nw_interp *const in) { varuint32 count; if (varuint32_read(f, &count)) { LOG("%s: varuint32_read failed\n", __func__); return -1; } for (varuint32 i = 0; i < count; i++) { struct global_type g; if (check_global_type(f, &g)) { LOG("%s: check_global_type\n", __func__); return -1; } else if (in && add_global(in, &g)) { LOG("%s: add_global failed\n", __func__); return -1; } } return 0; } static int check(FILE *const f, const unsigned long len) { const long start = ftell(f); if (start < 0) { LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); return -1; } else if (check_global_types(f, NULL)) { LOG("%s: check_global_types failed\n", __func__); return -1; } const long end = ftell(f); if (end < 0) { LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); return -1; } const unsigned long size = end - start; if (size != len) { LOG("%s: size exceeded (%lu expected, got %lu)\n", __func__, len, size); return -1; } return 0; } int section_global_check(const struct section *const s, struct nw_mod *const m, const unsigned long len) { FILE *const f = s->f; if (m->sections.global) { LOG("%s: ignoring duplicate section\n", __func__); return fseek(f, len, SEEK_CUR); } const long offset = ftell(f); if (offset < 0) { LOG("%s: ftell(3): %s\n", __func__, strerror(errno)); return -1; } else if (check(f, len)) { LOG("%s: check failed\n", __func__); return -1; } m->sections.global = offset; return 0; } int section_global_push(FILE *const f, const struct nw_mod *const m, struct nw_interp *const i) { if (fseek(f, m->sections.global, SEEK_SET)) { LOG("%s: fseek(3): %s\n", __func__, strerror(errno)); return -1; } else if (check_global_types(f, i)) { LOG("%s: check_global_types failed\n", __func__); return -1; } return 0; }