diff --git a/name-emu/Cargo.toml b/name-emu/Cargo.toml index 381ed452..62a589e3 100644 --- a/name-emu/Cargo.toml +++ b/name-emu/Cargo.toml @@ -12,4 +12,5 @@ base64 = "0.21.4" byteorder = "1.4.3" serde_json = "1.0.107" serde = { version = "1.0.188", features = ["derive"] } -toml = "0.7.6" \ No newline at end of file +toml = "0.7.6" +elf = "0.7.3" \ No newline at end of file diff --git a/name-emu/src/exception.rs b/name-emu/src/exception.rs index 3c04051e..0e07cab4 100644 --- a/name-emu/src/exception.rs +++ b/name-emu/src/exception.rs @@ -19,6 +19,10 @@ pub enum ExecutionErrors { // Can also refer to underflow IntegerOverflow { rt: usize, rs: usize, value1: u32, value2: u32 }, + SyscallInvalidArugment, + + SyscallInvalidSyscallNumber, + Event { event: ExecutionEvents } } @@ -97,6 +101,7 @@ pub fn exception_pretty_print(reason: Result<(), ExecutionErrors>) -> ExceptionI type_name: None, full_type_name: None, evaluate_name: None, stack_trace: None, inner_exception: None }) }, + _ => unimplemented!("adf"), } } diff --git a/name-emu/src/main.rs b/name-emu/src/main.rs index 9005675d..ce62664e 100644 --- a/name-emu/src/main.rs +++ b/name-emu/src/main.rs @@ -1,9 +1,12 @@ +use std::collections::HashMap; use std::fs::File; use std::io::{BufReader, BufWriter, Write}; use dap::events::{StoppedEventBody, ExitedEventBody, TerminatedEventBody}; use dap::responses::{ReadMemoryResponse, SetExceptionBreakpointsResponse, ThreadsResponse, StackTraceResponse, ScopesResponse, VariablesResponse, ContinueResponse}; use dap::types::{StoppedEventReason, Thread, StackFrame, Scope, Source, Variable}; +use elf::endian::{AnyEndian, LittleEndian}; +use elf::section::SectionHeader; use thiserror::Error; use dap::prelude::*; @@ -17,10 +20,16 @@ use exception::{ExecutionErrors, exception_pretty_print, ExecutionEvents}; mod lineinfo; use lineinfo::{LineInfo, lineinfo_import}; +mod syscall; + use base64::{Engine as _, engine::general_purpose}; use std::env; use std::net::TcpListener; +use elf::ElfBytes; +use elf::segment::ProgramHeader; +use elf::abi::PT_LOAD; + #[derive(Error, Debug)] enum MyAdapterError { #[error("Unhandled command")] @@ -38,20 +47,34 @@ enum MyAdapterError { type DynResult = std::result::Result>; -fn reset_mips(program_data: &[u8]) -> Mips { +fn reset_mips(elf_file: &Vec, elf_parsed: &ElfBytes<'_, LittleEndian>, segments: &Vec) -> Mips { // Reset execution and begin again. - let mut mips: Mips = Default::default(); + let mut mips: Mips = Default::default(); + mips.pc = elf_parsed.ehdr.e_entry as usize; - for (i, byte) in program_data.iter().enumerate() { - mips.write_b(mips::DOT_TEXT_START_ADDRESS + i as u32, *byte).unwrap(); + for phdr in segments { + mips.memories.push((elf_file[phdr.p_offset as usize .. (phdr.p_offset + phdr.p_filesz) as usize].to_vec(), phdr.p_vaddr as u32, phdr.p_filesz as u32)); + + // WARNING: BROKEN + if elf_parsed.ehdr.e_entry == phdr.p_vaddr { + mips.stop_address = (phdr.p_vaddr + phdr.p_filesz) as usize; + } } - mips.stop_address = mips::DOT_TEXT_START_ADDRESS as usize + program_data.len(); mips } fn main() -> DynResult<()> { + + let elf_file_data = std::fs::read("/home/qwe/Documents/CS4485/Fibonacci_linked").unwrap(); + let elf_file = ElfBytes::::minimal_parse(elf_file_data.as_slice()).unwrap(); + let elf_all_load_phdrs: Vec = elf_file.segments().unwrap() + .iter() + .filter(|phdr|{phdr.p_type == PT_LOAD}) + .collect(); + + let args_strings: Vec = env::args().collect(); if args_strings.len() != 5 { @@ -165,7 +188,7 @@ loop { server.send_event(Event::Initialized)?; - mips = reset_mips(&program_data); + mips = reset_mips(&elf_file_data, &elf_file, &elf_all_load_phdrs); } @@ -427,7 +450,7 @@ loop { } Command::Restart(_) => { - mips = reset_mips(&program_data); + mips = reset_mips(&elf_file_data, &elf_file, &elf_all_load_phdrs); let rsp = req.success( ResponseBody::Restart @@ -470,7 +493,9 @@ loop { } // OK, what happened? let stopped_event_body = match mips.prev_ins_result { - Ok(()) => unreachable!(), // It's unreachable. + Ok(()) => { + unreachable!()// It's unreachable. + } Err(what_happened) => match what_happened { ExecutionErrors::Event{event} => match event { ExecutionEvents::ProgramComplete => { diff --git a/name-emu/src/mips.rs b/name-emu/src/mips.rs index 5231a781..16f8de85 100644 --- a/name-emu/src/mips.rs +++ b/name-emu/src/mips.rs @@ -4,14 +4,12 @@ use std::io::Cursor; use std::fs::File; use std::io::Write; -use crate::exception::{ExecutionErrors, ExecutionEvents}; -pub const DOT_TEXT_START_ADDRESS: u32 = 0x00400000; -const DOT_TEXT_MAX_LENGTH: u32 = 0x1000; -const LEN_TEXT_INITIAL: usize = 200; +use crate::{exception::{ExecutionErrors, ExecutionEvents}, syscall::syscall}; + const MIPS_INSTRUCTION_LENGTH: usize = 4; -pub const REGISTER_NAMES: [&str; 32] = [ +pub const REGISTER_NAMES: [&str; 33] = [ "$zero", "$at", "$v0", @@ -43,9 +41,12 @@ pub const REGISTER_NAMES: [&str; 32] = [ "$gp", "$sp", "$fp", - "$ra" + "$ra", + // See below constant. Used for exception printing + "Immediate" ]; pub const PC_NAME: &str = "$pc"; +pub const IMMEDIATE_PRETTY_PRINT: usize = 32; #[derive(Debug)] enum BranchDelays { @@ -54,6 +55,12 @@ enum BranchDelays { Ready } +// https://www.reddit.com/r/rust/comments/6175al/arbitrary_width_sign_extension_in_rust/ +fn sign_extend(x: i32, nbits: u32) -> i32 { + let notherbits = std::mem::size_of_val(&x) as u32 * 8 - nbits; + x.wrapping_shl(notherbits).wrapping_shr(notherbits) +} + #[derive(Debug)] pub(crate) struct Mips { pub regs: [u32; 32], @@ -64,7 +71,7 @@ pub(crate) struct Mips { // Branch delay slots are implemented by filling this buffer with the // branch target, which will be triggered after the following instruction - branch_delay_target: u32, + branch_delay_target: usize, branch_delay_status: BranchDelays, @@ -91,13 +98,11 @@ impl Default for Mips { floats: [0f32; 32], mult_hi: 0, mult_lo: 0, - pc: DOT_TEXT_START_ADDRESS as usize, + pc: 0, branch_delay_target: 0, branch_delay_status: BranchDelays::NotActive, - memories: vec![ - (vec![0; LEN_TEXT_INITIAL], DOT_TEXT_START_ADDRESS, DOT_TEXT_MAX_LENGTH) - ], - stop_address: DOT_TEXT_START_ADDRESS as usize, + memories: vec![], + stop_address: 0, prev_ins_result: Ok(()) } } @@ -150,6 +155,16 @@ impl Mips { 0x2 => { self.regs[ins.rd] = self.regs[ins.rt] >> ins.shamt; } + // Jump Register + 0x8 => { + self.pc = self.regs[ins.rs] as usize; + } + // System Call + 0xC => { + // Grab 20-bit code field + let code = (opcode >> 6) & 0xFFFFF; + syscall(self, code)?; + } // Add 0x20 => { let result = self.regs[ins.rt].checked_add(self.regs[ins.rs]); @@ -204,11 +219,52 @@ impl Mips { } Ok(()) } + + // This is a helper function for doing branches + fn configure_branch(&mut self, ins: Itype) { + self.branch_delay_target = self.pc.wrapping_add_signed(sign_extend(((ins.imm as u32) << 2) as i32, 18) as isize); + self.branch_delay_status = BranchDelays::Set; + } + fn dispatch_i(&mut self, ins: Itype, opcode: u32) -> Result<(), ExecutionErrors> { - let memory_address = (ins.rt as i64 + (ins.imm as i64)) as u32; + let memory_address = self.regs[ins.rs].wrapping_add(ins.imm as u32); match ins.opcode { + // Branch on Less than Zero + // MIPS manual says: If the contents of GPR rs are less than zero (sign bit is 1) + 0x6 => { + if self.regs[ins.rs] as i32 <= 0 { + self.configure_branch(ins); + } + } + // Branch on Greater than Zero + // MIPS manual says: If the contents of GPR rs + // are greater than zero (sign bit is 0 but value not zero) + 0x7 => { + if self.regs[ins.rs] > 0 { + self.configure_branch(ins); + } + } + // Add Immediate + 0x8 => { + let result = self.regs[ins.rs].checked_add_signed(ins.imm as i16 as i32); + match result { + Some(value) => {self.regs[ins.rt] = value;} + None => { + return Err(ExecutionErrors::IntegerOverflow { + rt: IMMEDIATE_PRETTY_PRINT, + rs: ins.rs, + value1: ins.imm as u32, + value2: self.regs[ins.rs] + }); + } + } + } + // Add Immediate Unsigned + 0x9 => { + self.regs[ins.rt] = self.regs[ins.rs].wrapping_add(ins.imm as u32); + } // Set on Less Than Immediate (signed) // If rs is less than sign-extended 16 bit immediate using signed comparison, then set rt to 1 // Casting on imm is to sign extend. See load byte casts @@ -274,15 +330,13 @@ impl Mips { // Branch if Equal 0x4 => { if self.regs[ins.rt] == self.regs[ins.rs] { - self.branch_delay_target = (ins.imm as u32) << 2; - self.branch_delay_status = BranchDelays::Set; + self.configure_branch(ins); } } // Branch if Not Equal 0x5 => { if self.regs[ins.rt] != self.regs[ins.rs] { - self.branch_delay_target = (ins.imm as u32) << 2; - self.branch_delay_status = BranchDelays::Set; + self.configure_branch(ins); } } @@ -299,14 +353,16 @@ impl Mips { // Jump absolute 2 => { self.branch_delay_status = BranchDelays::Set; - self.branch_delay_target = self.pc as u32 & 0xF0000000 | (ins.dest << 2); + self.branch_delay_target = (self.pc as u32 & 0xF0000000 | (ins.dest << 2)) as usize; } // Jump And Link 3 => { self.branch_delay_status = BranchDelays::Set; - self.branch_delay_target = self.pc as u32 & 0xF0000000 | (ins.dest << 2); + self.branch_delay_target = (self.pc as u32 & 0xF0000000 | (ins.dest << 2)) as usize; // $ra = register 31 - self.regs[31] = self.pc as u32 + 8; + + // CODE SMELL— This has to be adjusted when branch delays are turned off!! + self.regs[31] = self.pc as u32 + 4; } _ => return Err(ExecutionErrors::UndefinedInstruction {instruction: opcode}) } @@ -316,7 +372,7 @@ impl Mips { fn decode(&self, instruction: u32) -> Instructions { let opcode = instruction >> 26 & 0b111111; - match opcode { + match opcode { // R-type 0 => { Instructions::R(Rtype { @@ -392,6 +448,15 @@ impl Mips { self.read_b(address + 2)?, self.read_b(address + 3)?]; Ok(Cursor::new(bytes).read_u32::().unwrap()) } + // Returns a reference to a block of memory, if it exists. + pub fn read_block(&mut self, address: u32, len: u32) -> Result<&[u8], ExecutionErrors> { + if let Some((memory, offset)) = self.map_memory(address) { + if memory.len() <= (len + offset) as usize { + return Ok(&memory[offset as usize .. (offset + len) as usize]); + } + } + Err(ExecutionErrors::MemoryIllegalAccess { load_address: address }) + } // Writes one byte @@ -413,7 +478,7 @@ impl Mips { let mut bytes = vec![]; bytes.write_u16::(value).unwrap(); self.write_b(address, bytes[0])?; - self.write_b(address, bytes[1])?; + self.write_b(address + 1, bytes[1])?; Ok(()) } // Writes a word in little endian form @@ -421,11 +486,21 @@ impl Mips { let mut bytes = vec![]; bytes.write_u32::(value).unwrap(); self.write_b(address, bytes[0])?; - self.write_b(address, bytes[1])?; - self.write_b(address, bytes[2])?; - self.write_b(address, bytes[3])?; + self.write_b(address + 1, bytes[1])?; + self.write_b(address + 2, bytes[2])?; + self.write_b(address + 3, bytes[3])?; Ok(()) } + // Writes a block of memory, if it exists + pub fn write_block(&mut self, address: u32, len: u32, data: &[u8]) -> Result<(), ExecutionErrors> { + if let Some((memory, offset)) = self.map_memory(address) { + if memory.len() <= (len + offset) as usize { + memory[offset as usize .. (offset + len) as usize].copy_from_slice(data); + return Ok(()) + } + } + Err(ExecutionErrors::MemoryIllegalAccess { load_address: address }) + } pub fn step_one(&mut self, mut f :&mut File) -> Result<(), ExecutionErrors> { let opcode = self.read_w(self.pc as u32)?; @@ -459,7 +534,7 @@ impl Mips { BranchDelays::NotActive => (), BranchDelays::Set => self.branch_delay_status = BranchDelays::Ready, BranchDelays::Ready => { - self.pc = self.branch_delay_target as usize; + self.pc = self.branch_delay_target; self.branch_delay_status = BranchDelays::NotActive; } } diff --git a/name-emu/src/syscall.rs b/name-emu/src/syscall.rs new file mode 100644 index 00000000..757b4355 --- /dev/null +++ b/name-emu/src/syscall.rs @@ -0,0 +1,38 @@ +// This file implements the system calls used by MARS. +// The system call number is stored in $v0, which is register 2. + +use crate::mips::Mips; +use crate::exception::{ExecutionErrors, ExecutionEvents}; +use std::io::Stdin; + +pub(crate) fn syscall(mips: &mut Mips, code: u32) -> Result<(), ExecutionErrors> { + match mips.regs[2] { + + // Print integer. Writes the value in $a0 to the screen + 1 => { + print!("{}", mips.regs[4]); + Ok(()) + } + // Print string. Writes null-terminated string pointed to by $a0 to the screen + 4 => { + let mut str_vec = vec![]; + let mut i = 0; + loop { + let read_char = mips.read_b(mips.regs[4] + i)?; + if read_char == 0 { + break; + } + str_vec.push(read_char as char); + i += 1; + }; + print!("{}", str_vec.iter().collect::()); + Ok(()) + } + // Exit. Immediately raise a ProgramComplete error + 10 => { + Err(ExecutionErrors::Event { event: ExecutionEvents::ProgramComplete }) + } + + _ => Err(ExecutionErrors::SyscallInvalidSyscallNumber) + } +} \ No newline at end of file