bool vm_interrupt(VMState *state, VMInterruptType type, ...) { va_list args; VMInterruptItem *item; unsigned int cycles; if (state->interrupt_policy == VM_POLICY_INTERRUPT_NEVER) return true; va_start(args, type); item = NULL; switch (type) { case VM_INTERRUPT_TIMER: cycles = va_arg(args, unsigned int); item = vm_new_interrupt_item(VM_INTERRUPT_TIMER, state->cycles + cycles); break; default: vm_seterrno(VM_NO_SUCH_INTERRUPT_TYPE_SUPPORT_ERROR); return false; } if(!item) return false; item->next = state->interrupts; state->interrupts = item; va_end(args); return true; }
static OPCODE_TYPE * _get_location(VMState *state, VMInfoType type, size_t addr) { OPCODE_TYPE *location = NULL; switch (type) { case VM_INFO_REGISTER: location = state->registers; if (addr >= nregisters) goto error; break; case VM_INFO_RAM: location = state->ram; if (addr >= ramsize) goto error; break; case VM_INFO_PIN: location = state->pins + pinoffset; if (addr >= npins) goto error; break; } return location + addr; error: vm_seterrno(VM_OUT_OF_BOUNDS_ERROR); return NULL; }
Opcode * get_opcode(VMState *state, PC_TYPE pc) { if (pc < state->executable_segment_offset || pc >= state->instructions_size) { vm_seterrno(VM_PC_OUT_OF_BOUNDS); #ifdef VM_DEBUG printf(LOCATION " pc: %lu max pc: %lu\n", (unsigned long) pc, (unsigned long) state->instructions_size - 1); #endif return NULL; } return state->instructions + pc; }
static char * _get_location(VMState *state, VMInfoType type, size_t addr, int *nbytes) { struct _mapping *mapping = get_info_type_mapping(type); if (addr < 0 ||addr >= mapping->end) { # ifdef VM_DEBUG printf("address: %lu, type: %s\n", addr, mapping->name); # endif vm_seterrno(VM_OUT_OF_BOUNDS_ERROR); return NULL; } /* Address within indicated address space. */ if (nbytes) *nbytes = mapping->size; return state->chunk + mapping->offset + addr; }
/* Parse an ELF file. See http://www.skyfree.org/linux/references/ELF_Format.pdf for a description of the ELF format. */ static bool _read_elf(VMState *state, char *program, size_t program_size) { Elf32_Ehdr *ehdr; char elfclass; ehdr = (Elf32_Ehdr *) program; if (ehdr->e_ident[EI_MAG0] == 0x7f && ehdr->e_ident[EI_MAG1] == 'E' && ehdr->e_ident[EI_MAG2] == 'L' && ehdr->e_ident[EI_MAG3] == 'F' && (elfclass = ehdr->e_ident[EI_CLASS]) != ELFCLASSNONE) { /* valid ELF file */ if (elfclass == ELFCLASS32) return _elf32_read(state, program, program_size); else return _elf64_read(state, program, program_size); } else { vm_seterrno(VM_NOT_ELF); return false; } }
/* Load the loadable segments of the program into the VMState. Disassemble The Executable Segment. */ static bool _elf32_read(VMState *state, char *program, size_t program_size) { Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; char *rom; int i; struct _mapping *rommapping = get_info_type_mapping(VM_INFO_ROM); rom = (char *) state->chunk + rommapping->offset; ehdr = (Elf32_Ehdr *) program; SETPC(state, ehdr->e_entry); if (!ehdr->e_phoff) { vm_seterrno(VM_NO_SEGMENTS); return false; } phdr = (Elf32_Phdr *) (program + ehdr->e_phoff); for (i = 0; i < ehdr->e_phnum; i++) { if (phdr->p_type & PT_LOAD) { char *startaddr; startaddr = rom + LOAD_ADDRESS; if (phdr->p_flags & PF_X) { /* executable segment, disassemble */ if (state->instructions) { /* Will there ever be multiple executable segments? Text and data segments may both be executable. */ vm_seterrno(VM_MULTIPLE_EXECUTABLE_SEGMENTS); return false; } state->instructions_size = (phdr->p_filesz / sizeof(OPCODE_TYPE)); state->instructions = disassemble( (OPCODE_TYPE *) (program + phdr->p_offset), state->instructions_size); state->executable_segment_offset = LOAD_ADDRESS; } if (LOAD_ADDRESS + phdr->p_memsz > rommapping->end - rommapping->offset) { vm_seterrno(VM_ERROR_PROGRAM_TOO_BIG); return false; } /* load segment into the ROM of the VM */ memcpy(startaddr, program + phdr->p_offset, phdr->p_filesz); /* NUL the rest */ memset(startaddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } phdr++; } if (!_vm_errno && !state->instructions) vm_seterrno(VM_NO_EXECUTABLE_SEGMENT); return (bool) state->instructions; }
static bool _vm_step(VMState *state, int nsteps, VMStateDiff **diff, bool *hit_bp, bool first) { Opcode *opcode; opcode_handler *handler; VMInterruptCallable *callable; VMInterruptItem *interrupt_item, *previous_interrupt_item = NULL; VMStateDiff *newdiff = NULL; #define RETURN(x) do { RELEASE_STATE(state); return (x); } while(0) *hit_bp = false; while (nsteps > 0) { /* acquire and release for every step. This allows for some nice contention! */ ACQUIRE_STATE(state); if (state->break_async) { RETURN(true); } callable = state->interrupt_callables; while (callable) { if (!callable->func(state, callable->argument)) { vm_seterrno(VM_INTERRUPT_CALLABLE_ERROR); RETURN(false); } callable = callable->next; } if (!first && _hit_breakpoint(state)) { /* breakpoint */ *hit_bp = true; RETURN(true); } else { /* PC error checking */ { /* offsets in bytes */ size_t pc; pc = GETPC(state); if (pc < state->executable_segment_offset || pc >= state->instructions_size) { vm_seterrno(VM_PC_OUT_OF_BOUNDS); #ifdef VM_DEBUG printf(LOCATION " pc: %lu max pc: %lu\n", (unsigned long) pc, (unsigned long) state->instructions_size - 1); #endif RETURN(false); } } /* Save changes in diff */ if (diff) { if (!(newdiff = vm_newdiff())) RETURN(false); newdiff->next = *diff; newdiff->pc = state->registers[PC]; newdiff->cycles = state->cycles; *diff = newdiff; } if (state->interrupt_policy != VM_POLICY_INTERRUPT_NEVER) { if (state->interrupts != NULL) { /* There are interrupts specified in the queue, call the user-written set_interrupt callback. */ if (!set_interrupt(state, newdiff)) RETURN(false); } /* For every step, handle an interrupt in the debuggee (if present) bytes calling a user-written function (or a default noop). */ if (!handle_interrupt(state, newdiff)) { RETURN(false); } } /* Execute instruction */ opcode = (Opcode *) OPCODE(state); handler = opcode_handlers[opcode->opcode_index].handler; if (!handler(state, newdiff, opcode->instruction)) { RETURN(state->stopped_running); } } nsteps--; first = false; RELEASE_STATE(state); } return true; #undef RETURN }
static bool _vm_step(VMState *state, unsigned long nsteps, VMStateDiff **diff, bool *hit_bp, bool first) { VMInterruptCallable *callable; VMStateDiff *newdiff = NULL; #define RETURN(x) do { RELEASE_STATE(state); return (x); } while(0) *hit_bp = false; while (nsteps > 0) { /* acquire and release for every step. This allows for some nice contention! */ ACQUIRE_STATE(state); if (state->break_async) { RETURN(true); } callable = state->interrupt_callables; while (callable) { if (!callable->func(state, callable->argument)) { vm_seterrno(VM_INTERRUPT_CALLABLE_ERROR); RETURN(false); } callable = callable->next; } if (!first && _hit_breakpoint(state)) { /* breakpoint */ *hit_bp = true; RETURN(true); } else { Opcode *opcode; opcode_handler *handler; /* Do the PC validity checks before updating any diffs. */ if (!(opcode = get_opcode(state, GETPC(state)))) RETURN(false); /* Save changes in diff */ if (diff) { if (!(newdiff = vm_newdiff())) RETURN(false); newdiff->next = *diff; newdiff->pc = GETPC(state); newdiff->cycles = state->cycles; *diff = newdiff; } if (state->interrupt_policy != VM_POLICY_INTERRUPT_NEVER) { if (state->interrupts != NULL) { /* There are interrupts specified in the queue, call the user-written set_interrupt callback. */ if (!set_interrupt(state, newdiff)) RETURN(false); } /* For every step, handle an interrupt in the debuggee (if present) bytes calling a user-written function (or a default noop). */ if (!handle_interrupt(state, newdiff)) { RETURN(false); } } /* Execute instruction */ handler = opcode_handlers[opcode->opcode_index].handler; if (!handler(state, newdiff, opcode->instruction)) RETURN(state->stopped_running); } nsteps--; first = false; RELEASE_STATE(state); } return true; #undef RETURN }