msim done (ish)
This commit is contained in:
parent
96c67f2e71
commit
4af200b001
8 changed files with 995 additions and 3 deletions
79
msim/debug.c
Normal file
79
msim/debug.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include <merror.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <melf.h>
|
||||
|
||||
#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;
|
||||
}
|
56
msim/fault.c
Normal file
56
msim/fault.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include "fault.h"
|
||||
#include "sim.h"
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
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);
|
||||
}
|
10
msim/fault.h
Normal file
10
msim/fault.h
Normal file
|
@ -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__ */
|
373
msim/ins.c
Normal file
373
msim/ins.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
#include <mips.h>
|
||||
#include <melf.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
226
msim/load.c
Normal file
226
msim/load.c
Normal file
|
@ -0,0 +1,226 @@
|
|||
#include <merror.h>
|
||||
#include <elf.h>
|
||||
#include <melf.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#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;
|
||||
}
|
74
msim/main.c
74
msim/main.c
|
@ -1,5 +1,73 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <merror.h>
|
||||
#include <signal.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
93
msim/sim.c
Normal file
93
msim/sim.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
#include <merror.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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;
|
||||
}
|
87
msim/sim.h
Normal file
87
msim/sim.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* Copyright (c) 2024 Freya Murphy */
|
||||
|
||||
#ifndef __SIM_H__
|
||||
#define __SIM_H__
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/// 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__ */
|
||||
|
Loading…
Reference in a new issue