thesis-lisp/vm/load.c

274 lines
7.0 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "binary.h"
#include "error.h"
#include "list.h"
#include "load.h"
#include "unit.h"
#include "vmstate.h"
#include "vmval.h"
static int c_print(struct vm_state *vm) {
uint64_t w;
int res;
if ((res = vm_pop(vm, &w)) != 0) {
return res;
}
vm_print(w);
printf("\n");
vm_unref(w);
return 0;
}
static struct vm_export_entry c_unit_core_exports[] = {
{ 1, { (uintptr_t) c_print }, "print" }
};
static int load_core(struct vm_unit_info *info) {
struct vm_export_entry *ent;
assert(info);
vector_init(&info->exports, sizeof(struct vm_export_entry));
for (size_t i = 0; i < sizeof(c_unit_core_exports) / sizeof(struct vm_export_entry); ++i) {
ent = vector_append(&info->exports);
if (!ent) {
return -ERR_OUT_OF_MEMORY;
}
memcpy(ent, &c_unit_core_exports[i], sizeof(struct vm_export_entry));
}
info->unit = NULL;
info->index = 0xFFFFFFFF;
return 0;
}
static FILE *unit_try_open_in(const char *paths, const char *name) {
const char *head;
char path[256];
FILE *fp;
head = paths;
while (head) {
// strchrnul emulation
const char *end = strchr(head, ':');
size_t len;
if (end) {
len = end - head;
} else {
len = strlen(head);
}
strncpy(path, head, len);
snprintf(path + len, sizeof(path) - len, "/%s.vmx", name);
if ((fp = fopen(path, "rb")) != NULL) {
return fp;
}
if (!end) {
break;
}
head = end + 1;
}
return NULL;
}
static FILE *vm_open_unit(const char *name) {
static const char *const st_path = "/usr/lib/l2:/usr/local/lib/l2";
const char *env_path = getenv("L2PATH");
FILE *fp;
char path[256];
snprintf(path, sizeof(path), "./%s.vmx", name);
// Try current dir
if ((fp = fopen(path, "rb")) != NULL) {
return fp;
}
if ((fp = unit_try_open_in(env_path, name)) != NULL) {
return fp;
}
return unit_try_open_in(st_path, name);
}
static int vm_load_functions(struct vm_unit *unit, struct bin_header *hdr, FILE *fp) {
struct vm_func_entry *func;
struct bin_func_entry ent;
uint32_t *bytecode;
fseek(fp, hdr->func_table_offset, SEEK_SET);
for (size_t i = 0; i < hdr->func_table_size; ++i) {
fread(&ent, 1, sizeof(struct bin_func_entry), fp);
bytecode = malloc(ent.len);
if (!bytecode) {
return -ERR_OUT_OF_MEMORY;
}
fread(bytecode, 1, ent.len, fp);
func = unit_add_function(unit);
if (i == 0 && ent.local_count) {
return -ERR_IMAGE_INDEX;
}
if (ent.local_count > MAXLOC) {
return -ERR_IMAGE_INDEX;
}
func->argc = ent.argc;
func->bytecode = bytecode;
func->local_count = ent.local_count;
}
return 0;
}
static int vm_load_refs(struct vm_unit *unit,
struct list_head *refs,
struct bin_header *hdr,
FILE *fp) {
struct vector unit_names;
struct bin_ref_entry ref_ent;
struct bin_unit_entry unit_ent;
struct vm_unresolved_ref *ref;
char name[64];
vector_init(&unit_names, 64);
// Load unit reference table
fseek(fp, hdr->unit_table_offset, SEEK_SET);
for (size_t i = 0; i < hdr->unit_table_size; ++i) {
fread(&unit_ent, 1, sizeof(struct bin_unit_entry), fp);
if (unit_ent.name_len >= sizeof(name) - 1) {
return -ERR_IMAGE_INDEX;
}
fread(name, 1, unit_ent.name_len + 1, fp);
char *dst = vector_append(&unit_names);
if (!dst) {
return -ERR_OUT_OF_MEMORY;
}
strcpy(dst, name);
}
// Knowing unit_index:unit_name pairs, for
// unit_name:ref_name:ref_entry tuples for each external reference
fseek(fp, hdr->ref_table_offset, SEEK_SET);
for (size_t i = 0; i < hdr->ref_table_size; ++i) {
fread(&ref_ent, 1, sizeof(struct bin_ref_entry), fp);
if (ref_ent.name_len >= sizeof(name) - 1) {
return -ERR_IMAGE_INDEX;
}
fread(name, 1, ref_ent.name_len + 1, fp);
if (ref_ent.unit_index >= hdr->unit_table_size) {
return -ERR_IMAGE_INDEX;
}
ref = malloc(sizeof(struct vm_unresolved_ref));
if (!ref) {
return -ERR_OUT_OF_MEMORY;
}
ref->entry = unit_add_ref(unit);
if (!ref->entry) {
return -ERR_OUT_OF_MEMORY;
}
strcpy(ref->unit_name, vector_ref(&unit_names, ref_ent.unit_index));
strcpy(ref->sym_name, name);
list_add(&ref->link, refs);
}
vector_free(&unit_names);
return 0;
}
static int vm_load_exports(struct vm_unit_info *info,
struct bin_header *hdr,
FILE *fp) {
struct vm_export_entry *export;
struct bin_export_entry ent;
char name[64];
fseek(fp, hdr->export_table_offset, SEEK_SET);
for (size_t i = 0; i < hdr->export_table_size; ++i) {
fread(&ent, 1, sizeof(struct bin_export_entry), fp);
if (ent.name_len >= sizeof(name) - 1) {
return -ERR_IMAGE_INDEX;
}
fread(name, 1, ent.name_len + 1, fp);
export = vector_append(&info->exports);
if (!export) {
return -ERR_OUT_OF_MEMORY;
}
strcpy(export->name, name);
export->is_native = 0;
export->ex_index = ent.value;
}
return 0;
}
int vm_load_unit_file(struct vm_state *vm,
struct vm_unit_info *info,
struct list_head *refs,
FILE *fp) {
struct bin_header hdr;
struct vm_unit *unit;
int res;
fread(&hdr, 1, sizeof(struct bin_header), fp);
if (hdr.magic != IMAGE_MAGIC) {
return -ERR_IMAGE_MAGIC;
}
if (info) {
info->index = vm->units.size;
}
unit = vm_add_unit(vm, hdr.global_pool_size);
if (!unit) {
return -ERR_OUT_OF_MEMORY;
}
if ((res = vm_load_refs(unit, refs, &hdr, fp)) != 0) {
return res;
}
if ((res = vm_load_functions(unit, &hdr, fp)) != 0) {
return res;
}
if (info) {
vector_init(&info->exports, sizeof(struct vm_export_entry));
if ((res = vm_load_exports(info, &hdr, fp)) != 0) {
return res;
}
}
if (info) {
info->unit = unit;
}
return 0;
}
int vm_load_unit(struct vm_state *vm,
struct vm_unit_info *info,
struct list_head *refs,
const char *name) {
FILE *fp;
int res;
if (!strcmp(name, "core")) {
return load_core(info);
}
if ((fp = vm_open_unit(name)) == NULL) {
fprintf(stderr, "Unit not found: %s\n", name);
return -ERR_NOT_FOUND;
}
res = vm_load_unit_file(vm, info, refs, fp);
fclose(fp);
return res;
}