From 4af200b00188e02b2c6207dfe494a3dd12556c5f Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Mon, 30 Sep 2024 18:52:25 -0400 Subject: [PATCH] msim done (ish) --- msim/debug.c | 79 +++++++++++ msim/fault.c | 56 ++++++++ msim/fault.h | 10 ++ msim/ins.c | 373 +++++++++++++++++++++++++++++++++++++++++++++++++++ msim/load.c | 226 +++++++++++++++++++++++++++++++ msim/main.c | 74 +++++++++- msim/sim.c | 93 +++++++++++++ msim/sim.h | 87 ++++++++++++ 8 files changed, 995 insertions(+), 3 deletions(-) create mode 100644 msim/debug.c create mode 100644 msim/fault.c create mode 100644 msim/fault.h create mode 100644 msim/ins.c create mode 100644 msim/load.c create mode 100644 msim/sim.c create mode 100644 msim/sim.h diff --git a/msim/debug.c b/msim/debug.c new file mode 100644 index 0000000..1613c76 --- /dev/null +++ b/msim/debug.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include + +#include "sim.h" + +static uint32_t read_num() { + int i = 0; + while (1) { + int c = getc(stdin); + if (c < '0' || c > '9') { + ungetc(c, stdin); + break; + } + i *= 10; + i += c - '0'; + } + + if (i) + return i; + return 1; +} + +_Noreturn void sim_debug(struct simulator *sim) { + + int c; + sim->pc = sim->entry; + sim->current_pc = sim->pc; + +prompt: + printf(">> "); + fflush(stdout); +next: + switch ((c = getchar())) { + + /* skip whitespace */ + case ' ': + case '\t': + goto next; + + /* ignore nl */ + case '\n': + goto prompt; + + /* exit on eof */ + case EOF: + exit(0); + break; + + /* dump registers */ + case 'r': + sim_dump_reg(sim); + break; + + /* step ins */ + case 's': { + for (uint32_t i = read_num(); i > 0; i--) + sim_step(sim); + break; + } + + /* print curr ins */ + case 'i': + printf("0x%08x\n", B32(* (uint32_t *) (uintptr_t) sim->pc)); + break; + + case 'a': + printf("0x%08x\n", sim->pc); + break; + + default: + ERROR("unknown command '%c'", c); + break; + } + + goto next; +} diff --git a/msim/fault.c b/msim/fault.c new file mode 100644 index 0000000..88cd340 --- /dev/null +++ b/msim/fault.c @@ -0,0 +1,56 @@ +#include "fault.h" +#include "sim.h" +#include +#include + +static struct simulator *sim; + +static void sigsegv_handler(int sig, siginfo_t *info, void *ucontext) +{ + (void) sig; + (void) ucontext; + + const char *inner = ""; + switch (info->si_code) { + case SEGV_MAPERR: + inner = ": address not mapped to object"; + break; + case SEGV_ACCERR: + inner = ": invalid premissions for mapped object"; + break; + case SEGV_BNDERR: + inner = ": failed address bound checks"; + break; + case SEGV_PKUERR: + inner = ": access was denied by memory protection"; + break; + } + + sim_dump(sim, "page fault at %p%s", info->si_addr, inner); +} + +static void sigfpe_handler(int sig, siginfo_t *info, void *ucontext) +{ + (void) sig; + (void) info; + (void) ucontext; + + sim_dump(sim, "floating point exception"); + +} + +void init_handlers(struct simulator *s) +{ + struct sigaction act = {0}; + sim = s; + + /* sigsegv */ + act.sa_sigaction = sigsegv_handler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGSEGV, &act, NULL); + + /* sigfpe */ + act.sa_sigaction = sigfpe_handler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGFPE, &act, NULL); +} diff --git a/msim/fault.h b/msim/fault.h new file mode 100644 index 0000000..26371b3 --- /dev/null +++ b/msim/fault.h @@ -0,0 +1,10 @@ +/* Copyright (c) 2024 Freya Murphy */ + +#ifndef __FAULT_H__ +#define __FAULT_H__ + +#include "sim.h" + +void init_handlers(struct simulator *s); + +#endif /* __FAULT_H__ */ diff --git a/msim/ins.c b/msim/ins.c new file mode 100644 index 0000000..6a98e92 --- /dev/null +++ b/msim/ins.c @@ -0,0 +1,373 @@ +#include +#include +#include +#include +#include + +#include "sim.h" + +#define I16(n) ((int32_t)(uint16_t)(n)) + +static void sim_ins_special_sop30(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.shamt) { + case MIPS_SOP30_MUL: + sim->reg[ins.rd] = ((int64_t)sim->reg[ins.rs] / + (int64_t)sim->reg[ins.rt]) >> 0; + break; + + case MIPS_SOP30_MUH: + sim->reg[ins.rd] = ((int64_t)sim->reg[ins.rs] / + (int64_t)sim->reg[ins.rt]) >> 32; + break; + + default: + sim_dump(sim, "unknown sop30 funct (0b%06b)", ins.shamt); + } +} + +static void sim_ins_special_sop31(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.shamt) { + case MIPS_SOP31_MULU: + sim->reg[ins.rd] = ((uint64_t)sim->reg[ins.rs] / + (uint64_t)sim->reg[ins.rt]) >> 0; + break; + + case MIPS_SOP31_MUHU: + sim->reg[ins.rd] = ((uint64_t)sim->reg[ins.rs] / + (uint64_t)sim->reg[ins.rt]) >> 32; + break; + + default: + sim_dump(sim, "unknown sop31 funct (0b%06b)", ins.shamt); + } +} + +static void sim_ins_special_sop32(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.shamt) { + case MIPS_SOP32_DIV: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] / + (int32_t)sim->reg[ins.rt]; + break; + + case MIPS_SOP32_MOD: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] % + (int32_t)sim->reg[ins.rt]; + break; + + default: + sim_dump(sim, "unknown sop32 funct (0b%06b)", ins.shamt); + } +} + +static void sim_ins_special_sop33(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.shamt) { + case MIPS_SOP33_DIVU: + sim->reg[ins.rd] = sim->reg[ins.rs] / sim->reg[ins.rt]; + break; + + case MIPS_SOP33_MODU: + sim->reg[ins.rd] = sim->reg[ins.rs] % sim->reg[ins.rt]; + break; + + default: + sim_dump(sim, "unknown sop33 funct (0b%06b)", ins.shamt); + } +} + +static void sim_ins_special(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.funct) { + case MIPS_FUNCT_ADD: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] + + (int32_t)sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_ADDU: + sim->reg[ins.rd] = sim->reg[ins.rs] + sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_AND: + sim->reg[ins.rd] = sim->reg[ins.rs] & sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_SOP30: + sim_ins_special_sop30(sim, ins); + break; + + case MIPS_FUNCT_SOP31: + sim_ins_special_sop31(sim, ins); + break; + + case MIPS_FUNCT_SOP32: + sim_ins_special_sop32(sim, ins); + break; + + case MIPS_FUNCT_SOP33: + sim_ins_special_sop33(sim, ins); + break; + + case MIPS_FUNCT_JALR: + sim->reg[MIPS_REG_RA] = sim->pc; + /* fall through */ + case MIPS_FUNCT_JR: + sim->pc = sim->reg[ins.rs]; + break; + + case MIPS_FUNCT_MFHI: + sim->reg[ins.rd] = sim->hi; + break; + + case MIPS_FUNCT_MFLO: + sim->reg[ins.rd] = sim->low; + break; + + case MIPS_FUNCT_MTHI: + sim->hi = sim->reg[ins.rd]; + break; + + case MIPS_FUNCT_MTLO: + sim->low = sim->reg[ins.rd]; + break; + + case MIPS_FUNCT_SLL: + sim->reg[ins.rd] = sim->reg[ins.rt] << ins.shamt; + break; + + case MIPS_FUNCT_SLLV: + sim->reg[ins.rd] = sim->reg[ins.rt] << sim->reg[ins.rs]; + break; + + case MIPS_FUNCT_SLT: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] < (int32_t)sim->reg[ins.rt] ? 1 : 0; + break; + + case MIPS_FUNCT_SLTU: + sim->reg[ins.rd] = sim->reg[ins.rs] < sim->reg[ins.rt] ? 1 : 0; + break; + + case MIPS_FUNCT_SRA: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rt] >> ins.shamt; + break; + + case MIPS_FUNCT_SRAV: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rt] >> sim->reg[ins.rs]; + break; + + case MIPS_FUNCT_SRL: + sim->reg[ins.rd] = sim->reg[ins.rt] >> ins.shamt; + break; + + case MIPS_FUNCT_SRLV: + sim->reg[ins.rd] = sim->reg[ins.rt] >> sim->reg[ins.rs]; + break; + + case MIPS_FUNCT_SUB: + sim->reg[ins.rd] = (int32_t)sim->reg[ins.rs] - + (int32_t)sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_SUBU: + sim->reg[ins.rd] = sim->reg[ins.rs] - sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_SYSCALL: + sim->reg[MIPS_REG_V0] = syscall( + sim->reg[MIPS_REG_V0], + sim->reg[MIPS_REG_A0], + sim->reg[MIPS_REG_A1], + sim->reg[MIPS_REG_A2], + sim->reg[MIPS_REG_A3] + ); + break; + + case MIPS_FUNCT_OR: + sim->reg[ins.rd] = sim->reg[ins.rs] | sim->reg[ins.rt]; + break; + + case MIPS_FUNCT_NOR: + sim->reg[ins.rd] = !(sim->reg[ins.rs] | sim->reg[ins.rt]); + break; + + case MIPS_FUNCT_XOR: + sim->reg[ins.rd] = sim->reg[ins.rs] ^ sim->reg[ins.rt]; + break; + + default: + sim_dump(sim, "unknown funct (0b%05b)", ins.funct); + } +} + +static void sim_ins_regimm(struct simulator *sim, + union mips_instruction_data ins) +{ + switch (ins.bfunct) { + case MIPS_FUNCT_BGEZAL: + case MIPS_FUNCT_BGEZALL: + sim->reg[MIPS_REG_RA] = sim->pc; + /* fall through */ + case MIPS_FUNCT_BGEZ: + case MIPS_FUNCT_BGEZL: + if ((int32_t)sim->reg[ins.rs] >= 0) + sim->pc += ins.offset << 2; + break; + + case MIPS_FUNCT_BLTZAL: + case MIPS_FUNCT_BLTZALL: + sim->reg[MIPS_REG_RA] = sim->pc; + /* fall through */ + case MIPS_FUNCT_BLTZ: + case MIPS_FUNCT_BLTZL: + if ((int32_t)sim->reg[ins.rs] < 0) + sim->pc += ins.offset << 2; + break; + + default: + sim_dump(sim, "unknown bfunct (0b%06b)", ins.bfunct); + } +} + +void sim_ins(struct simulator *sim, uint32_t raw) +{ + // get ins parts + union mips_instruction_data ins = { + .raw = B32(raw) + }; + + // reset zero reg + sim->reg[MIPS_REG_ZERO] = 0; + + switch (ins.op) { + case MIPS_OP_SPECIAL: + sim_ins_special(sim, ins); + break; + + case MIPS_OP_REGIMM: + sim_ins_regimm(sim, ins); + break; + + case MIPS_OP_ADDI: + sim->reg[ins.rt] = (int32_t)sim->reg[ins.rs] + + (int16_t) ins.immd; + break; + + case MIPS_OP_ADDIU: + sim->reg[ins.rt] = sim->reg[ins.rs] + ins.immd; + break; + + case MIPS_OP_ANDI: + sim->reg[ins.rt] = sim->reg[ins.rs] & ins.immd; + break; + + case MIPS_OP_BALC: + sim->reg[MIPS_REG_RA] = sim->pc; + /* fall through */ + case MIPS_OP_BC: + sim->pc += ins.offs26 << 2; + break; + + case MIPS_OP_BEQ: + case MIPS_OP_BEQL: + if (sim->reg[ins.rs] == sim->reg[ins.rt]) + sim->pc += ins.offset << 2; + break; + + case MIPS_OP_BGTZ: + case MIPS_OP_BGTZL: + if ((int32_t)sim->reg[ins.rs] <= 0) + sim->pc += ins.offset << 2; + break; + + case MIPS_OP_BLEZ: + case MIPS_OP_BLEZL: + if ((int32_t)sim->reg[ins.rs] <= 0) + sim->pc += ins.offset << 2; + break; + + case MIPS_OP_BNE: + case MIPS_OP_BNEL: + if (sim->reg[ins.rs] != sim->reg[ins.rt]) + sim->pc += ins.offset << 2; + break; + + case MIPS_OP_JAL: + sim->reg[MIPS_REG_RA] = sim->pc; + /* fall through */ + case MIPS_OP_J: + sim->pc = ins.target << 2; + break; + + case MIPS_OP_LB: + sim->reg[ins.rt] = * (int8_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset); + break; + + case MIPS_OP_LBU: + sim->reg[ins.rt] = * (uint8_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset); + break; + + case MIPS_OP_LH: + sim->reg[ins.rt] = * (int16_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset); + break; + + case MIPS_OP_LHU: + sim->reg[ins.rt] = * (uint16_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset); + break; + + case MIPS_OP_LUI: + sim->reg[ins.rt] = ins.immd << 16; + break; + + case MIPS_OP_LW: + sim->reg[ins.rt] = * (uint32_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset); + break; + + case MIPS_OP_SB: + * (uint8_t *) (uintptr_t) (sim->reg[ins.rs] + + + ins.offset) = sim->reg[ins.rt]; + break; + + case MIPS_OP_SH: + * (uint16_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset) = sim->reg[ins.rt]; + break; + + case MIPS_OP_SW: + * (uint32_t *) (uintptr_t) (sim->reg[ins.rs] + + ins.offset) = sim->reg[ins.rt]; + break; + + case MIPS_OP_SLTI: + sim->reg[ins.rt] = (int32_t)sim->reg[ins.rs] < + (int16_t)ins.immd ? 1 : 0; + break; + + case MIPS_OP_SLTIU: + sim->reg[ins.rt] = sim->reg[ins.rs] < ins.immd ? 1 : 0; + break; + + case MIPS_OP_ORI: + sim->reg[ins.rt] = sim->reg[ins.rs] | ins.immd; + break; + + case MIPS_OP_XORI: + sim->reg[ins.rt] = sim->reg[ins.rs] ^ ins.immd; + break; + + default: + sim_dump(sim, "unknown op code (0b%05b)", ins.op); + } +} + diff --git a/msim/load.c b/msim/load.c new file mode 100644 index 0000000..77b62ed --- /dev/null +++ b/msim/load.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "sim.h" + +#define SEC_ALIGN 0x1000 + +struct load_state { + FILE *file; + int fd; + uint32_t file_sz; + + Elf32_Phdr *phdr; + uint32_t phdr_len; + Elf32_Ehdr ehdr; +}; + +static int assert_ehdr(const void *const given, const void *const assert, + size_t size) +{ + if (memcmp(given, assert, size) == 0) + return M_SUCCESS; + ERROR("invalid elf ehdr"); + return M_ERROR; +} + +static int load_ehdr(struct simulator *sim, struct load_state *state) +{ + Elf32_Ehdr ehdr; + fseek(state->file, 0, SEEK_SET); + if (fread(&ehdr, sizeof(Elf32_Ehdr), 1, state->file) != 1) { + ERROR("cannot load ehdr"); + return M_ERROR; + } + + /** + * Compare each "static" value in the ehdr and make sure it + * is what it should be. If not throw and error and eventually + * return. + */ + + Elf32_Ehdr baseline = MIPS_ELF_EHDR; + baseline.e_type = B16(ET_EXEC); + + #define EHDR_ASSERT(name, size) \ + if (res == M_SUCCESS) \ + res |= assert_ehdr(&baseline.e_##name, \ + &ehdr.e_##name, size) \ + + int res = 0; + EHDR_ASSERT(ident, EI_NIDENT); + EHDR_ASSERT(type, sizeof(Elf32_Half)); + EHDR_ASSERT(machine, sizeof(Elf32_Half)); + EHDR_ASSERT(version, sizeof(Elf32_Word)); + EHDR_ASSERT(flags, sizeof(Elf32_Word)); + EHDR_ASSERT(ehsize, sizeof(Elf32_Half)); + EHDR_ASSERT(phentsize, sizeof(Elf32_Half)); + EHDR_ASSERT(shentsize, sizeof(Elf32_Half)); + + #undef EHDR_ASSERT + + if (res) + return M_ERROR; + + state->ehdr = ehdr; + sim->entry = B32(ehdr.e_entry); + + return M_SUCCESS; +} + +static int load_phdr(struct load_state *state) +{ + Elf32_Phdr *phdr = NULL; + + uint32_t off = B32(state->ehdr.e_phoff); + uint16_t len = B16(state->ehdr.e_phnum); + + if (BOUND_CHK(state->file_sz, len, off)) { + ERROR("elf phdr location invalid"); + return M_ERROR; + } + + phdr = malloc(sizeof(Elf32_Phdr) * len); + if (phdr == NULL) { + PERROR("cannot alloc"); + return M_ERROR; + } + + fseek(state->file, off, SEEK_SET); + if (fread(phdr, sizeof(Elf32_Phdr), len, state->file) != len) { + free(phdr); + PERROR("cannot read phdr"); + return M_ERROR; + } + + state->phdr = phdr; + state->phdr_len = len; + return M_SUCCESS; +} + +static int load_segment(struct simulator *sim, + struct load_state *state, Elf32_Phdr *hdr) +{ + uint32_t off = B32(hdr->p_offset); + uint32_t len = B32(hdr->p_filesz); + uint32_t is_text = B32(hdr->p_flags) & PF_X; + + if (BOUND_CHK(state->file_sz, len, off)) { + ERROR("segment location invalid"); + return M_ERROR; + } + + uintptr_t addr = 0; + uint32_t add = 0; + + if (len % SEC_ALIGN) { + add = SEC_ALIGN - (len % SEC_ALIGN); + len += add; + } + + if (is_text) { + addr = sim->text_max; + sim->text_max += len; + } else { + addr = sim->data_max; + sim->data_max += len; + } + + bool read = B32(hdr->p_flags) & PF_R; + bool write = B32(hdr->p_flags) & PF_W; + + uint32_t prot = 0; + if (read) + prot |= PROT_READ; + if (write) + prot |= PROT_WRITE; + + void *res = mmap((void*)addr, len, prot, MAP_PRIVATE | MAP_FIXED, + state->fd, off); + + if ((uintptr_t)res != addr) { + PERROR("failed to map executable"); + return M_ERROR; + } + + return M_SUCCESS; +} + +static int load_memory(struct simulator *sim, struct load_state *state) +{ + uint32_t base = 0; + + for (uint32_t i = 0; i < state->phdr_len; i++) { + Elf32_Phdr *hdr = NULL; + uint32_t min = UINT32_MAX; + + if (B32(state->phdr[i].p_filesz) < 1) + continue; + + // we need to load segments in order + for (uint32_t j = 0; j < state->phdr_len; j++) { + Elf32_Phdr *temp = &state->phdr[j]; + uint32_t off = B32(temp->p_offset); + uint32_t len = B32(temp->p_filesz); + + if (len < 1) + continue; + + if (off <= base) + continue; + + if (off >= min) + continue; + + min = off; + hdr = temp; + } + + base = min; + + if (hdr == NULL) { + ERROR("invalid elf phdr"); + return M_ERROR; + } + + if (load_segment(sim, state, hdr)) + return M_ERROR; + } + + return M_SUCCESS; +} + +int sim_load_file(struct simulator *sim) { + struct load_state state; + + state.file = fopen(sim->args->executable, "r"); + if (state.file == NULL) { + PERROR("cannot read '%s'", state.file); + return M_ERROR; + } + state.fd = fileno(state.file); + + // get filesize + fseek(state.file, 0, SEEK_END); + state.file_sz = ftell(state.file); + state.phdr = NULL; + + int res = M_SUCCESS; + if (res == M_SUCCESS) + res = load_ehdr(sim, &state); + if (res == M_SUCCESS) + res = load_phdr(&state); + if (res == M_SUCCESS) + res = load_memory(sim, &state); + + if (state.phdr) + free(state.phdr); + fclose(state.file); + + return res; +} diff --git a/msim/main.c b/msim/main.c index 7afb34e..f8a0999 100644 --- a/msim/main.c +++ b/msim/main.c @@ -1,5 +1,73 @@ +#include +#include +#include +#include +#include -int main(void) { - // NOT YET IMPLEMENTED - return 1; +#include "fault.h" +#include "sim.h" + +static void help(void) { + printf("usage: msim [options] executable\n\n"); + printf("options:\n"); + printf("\t-h\t\tprints this help messaage\n"); + printf("\t-c\t\tdisable runtime memory checks (allows segfault)\n"); + printf("\t-d\t\truns the debugger\n"); +} + +int main(int argc, char **argv) { + + struct simulator sim; + struct simulator_args args = { + .executable = NULL, + .debug = false, + .memchk = true, + }; + + int c; + + while ((c = getopt(argc, argv, "hcd")) != 1) { + switch (c) { + case 'h': + help(); + return M_SUCCESS; + case 'c': + args.memchk = false; + break; + case 'd': + args.debug = true; + break; + case '?': + return M_ERROR; + default: + goto next; + } + } + +next: + if (optind < argc - 1) { + ERROR("too many executables passed"); + return M_ERROR; + } + + if (optind >= argc) { + ERROR("no executable passed"); + return M_ERROR; + } + + /// set file name + args.executable = argv[optind]; + + /// create handlers + init_handlers(&sim); + + /// init + if (sim_init(&sim, &args)) + return M_ERROR; + + /* _Noreturn */ + if (args.debug) + sim_debug(&sim); + else + sim_run(&sim); } diff --git a/msim/sim.c b/msim/sim.c new file mode 100644 index 0000000..9e0d518 --- /dev/null +++ b/msim/sim.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include + +#include "sim.h" +#include "melf.h" + +void sim_dump_reg(struct simulator *sim) +{ + const char *names[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", + "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", + "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", + "fp", "ra" + }; + + for (uint32_t i = 0; i < 8; i++) { + for (uint32_t j = 0; j < 4; j++) { + if (j != 0) + fputc(' ', stderr); + int idx = i+j*8; + fprintf(stderr, "$%s:\t0x%08x", names[idx], sim->reg[idx]); + } + fputc('\n', stderr); + } +} + +__attribute__((format(printf, 2, 3))) +_Noreturn void sim_dump(struct simulator *sim, const char *format, ...) { + + va_list list; + va_start(list, format); + + // header + fprintf(stderr, "\n\t\t!!! An exception has occurred !!!\n\n"); + fprintf(stderr, "\033[31merror: \033[0m"); + vfprintf(stderr, format, list); + fputc('\n', stderr); + + // pc + uint32_t pc = sim->current_pc; + fprintf(stderr, "pc: 0x%08x\n", pc); + + if (pc >= sim->text_min && pc < sim->text_max) + fprintf(stderr, "ins: 0x%08x\n", B32(* (uint32_t *) (uintptr_t) pc)); + + // registers + fprintf(stderr, "registers:\n"); + sim_dump_reg(sim); + + exit(1); +} + +void sim_step(struct simulator *sim) +{ + if (sim->pc < sim->text_min || sim->pc >= sim->text_max) + sim_dump(sim, "program counter reached non executable memory"); + + uint32_t ins = * (uint32_t *) (uintptr_t) sim->pc; + sim->pc += 4; + sim_ins(sim, ins); + sim->current_pc = sim->pc; +} + +_Noreturn void sim_run(struct simulator *sim) +{ + sim->pc = sim->entry; + sim->current_pc = sim->pc; + while (1) + sim_step(sim); +} + +int sim_init(struct simulator *sim, struct simulator_args *args) { + memset(sim->reg, 0, sizeof(uint32_t) * 32); + sim->data_max = MEM_DATA_ADDR; + sim->data_min = MEM_DATA_ADDR; + sim->text_max = MEM_TEXT_ADDR; + sim->text_min = MEM_TEXT_ADDR; + sim->args = args; + + // load executable + if (sim_load_file(sim)) + return M_ERROR; + + // check entry + if (sim->entry < sim->text_min || sim->entry >= sim->text_max) { + ERROR("elf entrypoint invalid"); + return M_ERROR; + } + + return M_SUCCESS; +} diff --git a/msim/sim.h b/msim/sim.h new file mode 100644 index 0000000..517babd --- /dev/null +++ b/msim/sim.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2024 Freya Murphy */ + +#ifndef __SIM_H__ +#define __SIM_H__ + +#include +#include + +/// arguments +struct simulator_args { + char *executable; + bool memchk; + bool debug; +}; + + +// when loading the executable, we need to bounds check to +// make sure its within the file size +// +// this checks that +// 1. the end is in the file +// 2. the off and len doesnt integer overflow +#define BOUND_CHK(size, len, off) \ + ((off) > UINT32_MAX - (len) || (off) + (len) > (size)) + +/// +/// data must start at these +/// addresses or the executable is +/// invalid +/// +#define MEM_TEXT_ADDR 0x00400000 +#define MEM_DATA_ADDR 0x10000000 + +struct simulator { + struct simulator_args *args; + + + /// the registers + uint32_t reg[32]; + + /// the program counter + uint32_t pc; + + /// the current executing pc + uint32_t current_pc; + + /// low and hi + uint32_t low; + uint32_t hi; + + /// memory address bounds + uint32_t data_min; + uint32_t data_max; + uint32_t text_min; + uint32_t text_max; + + // entrypoint + uint32_t entry; +}; + +/* initalize a simulator */ +int sim_init(struct simulator *sim, struct simulator_args *args); + +/* load a file into a simulator */ +int sim_load_file(struct simulator *sim); + +/* execute and instruction */ +void sim_ins(struct simulator *sim, uint32_t ins); + +/* execute the next instruction in the simulator */ +void sim_step(struct simulator *sim); + +/* run the simulator with the loaded state */ +_Noreturn void sim_run(struct simulator *sim); + +/* run the debugger for a simulator */ +_Noreturn void sim_debug(struct simulator *sim); + +/* panics the simulator */ +__attribute__((format(printf, 2, 3))) +_Noreturn void sim_dump(struct simulator *sim, const char *format, ...); + +/* dumps registers */ +void sim_dump_reg(struct simulator *sim); + +#endif /* __SIM_H__ */ +