From: Brendan Hansen Date: Tue, 14 Jun 2022 03:30:48 +0000 (-0500) Subject: spec-ed a byte code loader that an assembly can target X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=00196353218dcda6b36175389412b88bd0f83779;p=onyx-embedder.git spec-ed a byte code loader that an assembly can target --- diff --git a/docs/fileformat.md b/docs/fileformat.md new file mode 100644 index 0000000..0424fd9 --- /dev/null +++ b/docs/fileformat.md @@ -0,0 +1,40 @@ + +# Reloadable file format + +## Necessary sections + +- [ ] Header +- [ ] Code +- [ ] Data +- [ ] Func Desc +- [ ] Native Func Link + +### Header +"OVMI" +4-byte version + +### Code +Raw bytes that represent the instructions. +Array of 16-byte instructions. + +instr-count (32-bit LE integer) | instrs ... + +### Data +Where, how-long and what the data is. + +entry-count (32-bit LE integer) | (offset (32-bit) | size (32-bit) | data ...) ... + +### Func Desc +How many functions. +Where they start. +How many params each. +How many values each. + +func-count | (start-instr | param-count | value-count) ... + +### Native Func Link +How many native fucntions. +The names in numerical order. + +func-count | (param-count | name-len-with-null (32-bit integer) | name data (null-terminated)) ... + diff --git a/include/vm.h b/include/vm.h index e0b7325..04d15b9 100644 --- a/include/vm.h +++ b/include/vm.h @@ -52,13 +52,14 @@ struct ovm_program_t { }; ovm_program_t *ovm_program_new(ovm_store_t *store); -void ovm_program_delete(ovm_program_t *program); -void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs); -void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count); +void ovm_program_delete(ovm_program_t *program); +void ovm_program_add_instructions(ovm_program_t *program, i32 instr_count, ovm_instr_t *instrs); +void ovm_program_print_instructions(ovm_program_t *program, i32 start_instr, i32 instr_count); -void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count); -void ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count); -void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count); +void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i32 param_count, i32 value_number_count); +void ovm_program_begin_func(ovm_program_t *program, char *name, i32 param_count, i32 value_number_count); +void ovm_program_register_native_func(ovm_program_t *program, char *name, + void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count); // // Represents the running configuration and static @@ -76,6 +77,9 @@ ovm_engine_t *ovm_engine_new(ovm_store_t *store); void ovm_engine_delete(ovm_engine_t *engine); void ovm_engine_memory_copy(ovm_engine_t *engine, i64 target, void *data, i64 size); +bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename); +void ovm_program_link_native_funcs(ovm_program_t *program, ovm_native_func_t *funcs); + // // Represents ephemeral state / execution context. // If multiple threads are used, multiple states are needed. @@ -130,13 +134,15 @@ ovm_instr_t *ovm_func_add_instruction(ovm_func_t *func, ovm_instr_kind_t instr, void ovm_func_delete(ovm_func_t *func); struct ovm_native_func_t { + char *name; i32 param_count; void (*native_func)(void *userdata, ovm_value_t* params, ovm_value_t* result); void *userdata; }; -ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, i32 param_count, ovm_value_t *params); +ovm_value_t ovm_func_call(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program, i32 func_idx, + i32 param_count, ovm_value_t *params); void ovm_run_code(ovm_engine_t *engine, ovm_state_t *state, ovm_program_t *program); // diff --git a/src/cli.c b/src/cli.c index ea9a97e..199e8fc 100644 --- a/src/cli.c +++ b/src/cli.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { ovm_program_begin_func(prog, "add", 2, 6); ovm_program_add_instructions(prog, sizeof(func2_instrs) / sizeof(*func2_instrs), func2_instrs); - ovm_program_register_native_func(prog, print_result, NULL, 1); + ovm_program_register_native_func(prog, "print_result", print_result, NULL, 1); ovm_program_print_instructions(prog, 0, bh_arr_length(prog->code)); diff --git a/src/vm/program_loader.c b/src/vm/program_loader.c new file mode 100644 index 0000000..a8996ed --- /dev/null +++ b/src/vm/program_loader.c @@ -0,0 +1,102 @@ +#include "vm.h" + +// +// I'm very lazy and silly, so this code make the drastic assumption that the +// endianness of the machine that the file was built on and the machine the +// VM is running on are the SAME. If this is not the case, the integers very +// well could be f-ed up. Might be worth switching all integers to use ntohl, +// so at least that part is consistent... -brendanfh 06/13/2022 +// + +// +// I wish this didn't take the engine as a parameter... but currently the memory +// is stored on the engine. So unless the data section elements can be aggregated +// into an array to be applied later, this is best I got... +bool ovm_program_load_from_file(ovm_program_t *program, ovm_engine_t *engine, char *filename) { + bh_file file; + bh_file_error error = bh_file_open(&file, filename); + if (error != BH_FILE_ERROR_NONE) { + fprintf(stderr, "Failed to open '%s' for reading.\n", filename); + return false; + } + + char magic[4], version[4]; + bh_file_read(&file, magic, 4); + bh_file_read(&file, version, 4); + + if (strncmp(magic, "OVMI", 4) || strncmp(version, "\x00\x00\x00\x01", 4)) { + fprintf(stderr, "Mismatched version/magic number in '%s'.\n", filename); + return false; + } + + // + // Code section + // Just copy in the bytes directly. + // What's validation anyway? + // + i32 entry_count; + bh_file_read(&file, &entry_count, sizeof(i32)); + bh_arr_insert_end(program->code, entry_count); + bh_file_read(&file, &bh_arr_last(program->code), entry_count * sizeof(ovm_instr_t)); + + // + // Data section + // + bh_file_read(&file, &entry_count, sizeof(i32)); + fori (i, 0, entry_count) { + i32 offset, size; + bh_file_read(&file, &offset, sizeof(i32)); + bh_file_read(&file, &size, sizeof(i32)); + + assert(engine); + assert(engine->memory); + bh_file_read(&file, ((u8 *) engine->memory) + offset, size); + } + + // + // Func section + // + bh_file_read(&file, &entry_count, sizeof(i32)); + fori (i, 0, entry_count) { + i32 start_instr, param_count, value_number_count; + bh_file_read(&file, &start_instr, sizeof(i32)); + bh_file_read(&file, ¶m_count, sizeof(i32)); + bh_file_read(&file, &value_number_count, sizeof(i32)); + + ovm_program_register_func(program, "LOADED", start_instr, param_count, value_number_count); + } + + // + // Native link section + // + bh_file_read(&file, &entry_count, sizeof(i32)); + fori (i, 0, entry_count) { + i32 param_count, name_len; + bh_file_read(&file, ¶m_count, sizeof(i32)); + bh_file_read(&file, &name_len, sizeof(i32)); + + char *name_buf = bh_alloc_array(program->store->arena_allocator, char, name_len); + bh_file_read(&file, name_buf, name_len); + + ovm_program_register_native_func(program, name_buf, NULL, NULL, param_count); + } + + + return true; +} + +void ovm_program_link_native_funcs(ovm_program_t *program, ovm_native_func_t *funcs) { + bh_arr_each(ovm_native_func_t, nf, program->native_funcs) { + if (nf->native_func && nf->userdata) continue; + + ovm_native_func_t *func = funcs; + while (func->name) { + if (!strcmp(nf->name, func->name) && nf->param_count == func->param_count) { + nf->native_func = func->native_func; + nf->userdata = func->userdata; + } + + func++; + } + } +} diff --git a/src/vm/vm.c b/src/vm/vm.c index cf6ec4a..bb8bf6d 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -54,8 +54,11 @@ void ovm_program_register_func(ovm_program_t *program, char *name, i32 instr, i3 bh_arr_push(program->funcs, func); } -void ovm_program_register_native_func(ovm_program_t *program, void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) { +void ovm_program_register_native_func(ovm_program_t *program, char *name, + void (*func)(void *, ovm_value_t *, ovm_value_t *), void *data, i32 param_count) { + ovm_native_func_t native_func; + native_func.name = name; native_func.param_count = param_count; native_func.native_func = func; native_func.userdata = data;