diff options
Diffstat (limited to 'wai.c')
-rw-r--r-- | wai.c | 369 |
1 files changed, 369 insertions, 0 deletions
@@ -0,0 +1,369 @@ +/* + * SPDX-FileCopyrightText: 2025 orangerot <me@orangerot.dev> + * + * SPDX-License-Identifier: GPL-3.0 + * + * This program, named WAI, is a WebAssembly Interpreter. + * Compile using make. + * Usage: wai [FILE.wasm] + * + * Copyright (C) 2025 orangrot <me@orangerot.dev> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <endian.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> + +enum section { + Section_Custom, + Section_Type, + Section_Import, + Section_Function, + Section_Table, + Section_Memory, + Section_Global, + Section_Export, + Section_Start, + Section_Element, + Section_Code, + Section_Data, + Section_Data_Count, +}; + +#define STACK_CAPACITY 1024 +struct stack { + double items[STACK_CAPACITY]; + size_t count; +}; + + +struct module { + struct type_t *types; + char *funcs[128]; + struct table_t *tables; + struct mem_t *mems; + struct global_t *globals; + struct elem_t *elems; + struct data_t *datas; + struct start_t *start; + struct import_t *imports; + struct export_t *exports; + char *binary; + struct stack stack; + int scope; +}; + +enum INSTRUCTION { + INSTR_CALL = 0x10, + INSTR_ELSE = 0x05, + INSTR_END = 0x0b, + INSTR_F64 = 0x7C, + INSTR_F64_CONST = 0x44, + INSTR_F64_LT = 0x63, + INSTR_F64_MUL = 0xa2, + INSTR_F64_SUB = 0xa1, + INSTR_IF = 0x04, + INSTR_LOCAL_GET = 0x20, +}; + +enum TYPE { + TYPE_I32 = 0x7F, + TYPE_I64 = 0x7E, + TYPE_F32 = 0x7D, + TYPE_F64 = 0x7C, + TYPE_V128= 0x7B, + TYPE_FUNCREF = 0x70, + TYPE_EXTERNREF = 0x6F +}; + +#define incr(i, len) i++; if (i >= len) {return -1;} + +void stack_push(struct stack *s, double a) { + s->items[s->count++] = a; + printf("stack: "); + for (int i = 0; i < s->count; i++) { + printf("%f, ", s->items[i]); + } + printf("\n"); +} + +double stack_pop(struct stack *s) { + s->count--; + return s->items[s->count]; +} + +double stack_top(struct stack *s) { + return s->items[s->count-1]; +} + +int parse_type(char *binary, int len) { + int i = 0; + enum TYPE param = binary[i]; + printf("type %x\n", param); + incr(i, len); + switch (param) { + case TYPE_I32: + case TYPE_I64: + case TYPE_F32: + case TYPE_F64: + case TYPE_V128: + case TYPE_FUNCREF: + case TYPE_EXTERNREF: + break; + default: + return -1; + } + return i; +} + +int parse_function(struct module *module, char *binary, double param, int len); +int parse_instruction(struct module *module, char *binary, double param, int len) { + int i = 0; + enum INSTRUCTION instr = (u_char) binary[i]; + char *instr_addr = &binary[i]; + incr(i, len); + + switch (instr) { + case INSTR_CALL: + int a = binary[i]; + incr(i, len); + parse_function(module, module->funcs[a], stack_pop(&module->stack), len); + break; + case INSTR_ELSE: + printf("reached else instruction: impossible!\n"); + case INSTR_END: + break; + case INSTR_F64: + break; + case INSTR_F64_CONST: + double k = *(double*)&binary[i]; + stack_push(&module->stack, k); + i += 8; + break; + case INSTR_F64_LT: { + double b = stack_pop(&module->stack); + double a = stack_pop(&module->stack); + stack_push(&module->stack, a < b); + break; + } + case INSTR_F64_MUL: { + double b = stack_pop(&module->stack); + double a = stack_pop(&module->stack); + stack_push(&module->stack, a * b); + break; + } + case INSTR_F64_SUB: { + double b = stack_pop(&module->stack); + double a = stack_pop(&module->stack); + stack_push(&module->stack, a - b); + break; + } + case INSTR_IF: { + double a = stack_pop(&module->stack); + while (binary[i] != INSTR_ELSE) { + if (a) { + i += parse_instruction(module, &binary[i], param, len); + } else { + incr(i, len); + } + } + incr(i, len); + while (binary[i] != INSTR_END) { + if (a) { + incr(i, len); + } else { + i += parse_instruction(module, &binary[i], param, len); + } + } + incr(i, len); + break; + } + case INSTR_LOCAL_GET: + int local_index = binary[i]; + incr(i, len); + stack_push(&module->stack, param); + break; + default: + printf("unknown instruction! %x at %lx\n", instr, instr_addr - module->binary); + exit(1); + } + return i; +} + +int parse_function(struct module *module, char *binary, double param, int len) { + int i = 0; + int body_size = binary[i]; + incr(i, len); + // int local_decl_cound = binary[i]; + incr(i, len); + module->scope = 1; + while (binary[i] != INSTR_END) { + i += parse_instruction(module, &binary[i], param, len); + } + incr(i, len); + return i; +} + +int parse_section(struct module *module, char *binary, int len) { + int i = 0; + enum section type = binary[i]; + incr(i, len); + int size = binary[i]; + incr(i, len); + printf("section %x with size %d\n", type, size); + + switch ((enum section) type) { + case Section_Custom: + break; + case Section_Type: + printf("section: type\n"); + int num_types = binary[i]; + incr(i, len); + for (int type_i = 0; type_i < num_types; type_i++) { + if (binary[i] != 0x60) { + printf("expected function type, found %x\n", binary[i]); + return -1; + } + incr(i, len); + int num_params = binary[i]; + incr(i, len); + for (int params_i = 0; params_i < num_params; params_i++) { + i += (parse_type(&binary[i], len)); + } + int num_results = binary[i]; + incr(i, len); + for (int results_i = 0; results_i < num_results; results_i++) { + i += (parse_type(&binary[i], len)); + } + } + break; + case Section_Import: + break; + case Section_Function: + printf("section: function\n"); + int num_functions = binary[i]; + incr(i, len); + for (int function_i = 0; function_i < num_functions; function_i++) { + incr(i, len); + } + break; + case Section_Table: + break; + case Section_Memory: + break; + case Section_Global: + break; + case Section_Export: + printf("section: exports\n"); + int num_exports = binary[i]; + incr(i, len); + for (int exports_i = 0; exports_i < num_exports; exports_i++) { + int string_len = binary[i]; + incr(i, len); + printf("export name: "); + for (int si = 0; si < string_len; si++) { + putchar(binary[i]); + incr(i, len); + } + putchar('\n'); + // export kind + incr(i, len); + // export func index + incr(i, len); + } + break; + case Section_Start: + break; + case Section_Element: + break; + case Section_Code: + printf("section: code\n"); + int num_functions2 = binary[i]; + incr(i, len); + for (int function_i = 0; function_i < num_functions2; function_i++) { + module->funcs[function_i] = &binary[i]; + i += parse_function(module, &binary[i], 4, len); + } + printf("result: %f\n", module->stack.items[0]); + break; + case Section_Data: + break; + case Section_Data_Count: + break; + default: + fprintf(stderr, "expectet section\n"); + exit(1); + } + + if (size == 0x0) {incr(i, len);} + return i; +} + +int parse_module(char *binary, size_t len) { + int i = 0; + char *magic = "\0asm"; + while (i < 4) { + if (binary[i] != magic[i]) { + fprintf(stderr, "no wasm magic\n"); + return 0; + } + incr(i, len); + } + printf("magic found\n"); + printf("wasm version: %d\n", le32toh(*(int*)&binary[i])); + i += 4; + printf("addr %d\n", i); + + struct module module = {0}; + module.binary = binary; + + while (i < len) { + i += parse_section(&module, &binary[i], len); + } + return i; +} + + +int main(int argc, char **argv) { + + if (argc != 2) { + fprintf(stderr, "Usage: %s [file]\n", argv[0]); + return 1; + }; + FILE *file = fopen(argv[1], "r"); + if (file == NULL) { + fprintf(stderr, "Failed to open file\n"); + fclose(file); + return 1; + } + struct stat st; + stat(argv[1], &st); + printf("size: %ld\n", st.st_size); + + char *binary = malloc(st.st_size); + fread(binary, st.st_size, st.st_size, file); + + if (parse_module(binary, st.st_size) == -1) { + printf("error :(\n"); + } + + free(binary); + fclose(file); + return 0; +} + |