WAI/wai.c

370 lines
8.7 KiB
C

/*
* 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;
u_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;
u_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(u_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, u_char *binary, double param, int len);
int parse_instruction(struct module *module, u_char *binary, double param, int len) {
int i = 0;
enum INSTRUCTION instr = (u_char) binary[i];
u_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, u_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, u_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(u_char *binary, size_t len) {
int i = 0;
u_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);
unsigned 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;
}