From b4b7eb69c27414e944e77f3fd3bf34d9c0285433 Mon Sep 17 00:00:00 2001 From: Quentin Legot Date: Fri, 31 Mar 2023 15:18:58 +0200 Subject: [PATCH] Add elf header parsing --- src/simulator/loader.rs | 198 ++++++++++++++++++++++++++++++++++------ 1 file changed, 168 insertions(+), 30 deletions(-) diff --git a/src/simulator/loader.rs b/src/simulator/loader.rs index 4ed2b64..2f8a292 100644 --- a/src/simulator/loader.rs +++ b/src/simulator/loader.rs @@ -1,40 +1,11 @@ use crate::Machine; +use std::f32::consts::E; use std::fs; use std::io; use std::io::BufRead; use std::io::Read; - - - /// Load a file into a new machine - /// - /// `panic!` when size is not 1, 2, 4 or 8 - /// `panic!` when the text does not represents instructions in hexadecimal - /// - /// ### Parameters - /// - /// - **path** the path of the file to load - /// - **size** the number of bytes to write (1, 2, 4 or 8) -#[deprecated] -pub fn _load(path : &str, instruction_size: i32) -> Machine { - let file = fs::File::open(path).expect("Wrong filename"); - let reader = io::BufReader::new(file); - let mut machine = Machine::init_machine(); - - for (i,line) in reader.lines().enumerate() { - let res = u64::from_str_radix(&line.unwrap(), 16); - match res { - Ok(value) => { - Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value); - }, - _ => panic!() - } - } - println!("{:x}", Machine::read_memory(& mut machine, 4, 0)); - machine -} - /// load a 32-bits binary file into the machine /// /// ### Parameters @@ -62,4 +33,171 @@ pub fn load(path: &str, machine: &mut Machine, start_index: usize) -> Result<(), // #[cfg(debug_assertions)] // println!("{:04x?}", instructions); // only print loaded program in debug build Ok(()) +} + +struct Loader { + bytes: Vec +} + +impl Loader { + + pub fn load(path: &str, machine: &mut Machine, start_index: usize) -> Result { + let mut file = fs::File::open(path)?; + let mut instructions: Vec = Default::default(); + loop { + let mut buf: [u8; 1] = [0; 1]; + let res = file.read(&mut buf)?; + if res == 0 { + break; // eof + } else { + instructions.push(buf[0]); + } + } + // #[cfg(debug_assertions)] + // println!("{:04x?}", instructions); // only print loaded program in debug build + Ok(Self { bytes: instructions }) + } + + fn parse(&mut self) -> Result<(), ()> { + todo!(); + Ok(()) + } + + /// return true if the 4 first bytes constitude the elf magic number + fn is_elf(&self) -> bool { + self.bytes.get(0..4) == Option::Some(&[0x7f, 0x45, 0x4c, 0x46]) + } + + /// return true if big endian, false otherwise + fn check_endianess(&self) -> bool { + self.bytes.get(5) == Option::Some(&2) + } + + /// return true if file is 32 bits, false if 64 bits + fn is_32bits(&self) -> bool { + self.bytes.get(4) == Option::Some(&1) + } + + /// return the version of the elf file (should be 1) + /// Can be None if the file is smaller than 7 bytes -> the file is invalid + fn get_version(&self) -> Option { + self.bytes.get(6).copied() // work as primitives implements Copy + } + + /// return true if target abi of the binary file is System V, false otherwise + fn is_system_v_elf(&self) -> bool{ + self.bytes.get(7) == Option::Some(&0) + } + + /// return true if specified target instruction set architecture is RISCV + fn is_riscv_isa(&self) -> bool { + self.bytes.get(0x12) == Option::Some(&0xf7) + } + + /// memory address of the entry point from where the process starts its execution + fn get_entrypoint(&self, is_32bits: bool) -> Option { + if is_32bits { + self.get_address_point(0x18, true) + } else { + self.get_address_point(0x18, false) + } + } + + /// Memory address of the start of the program header table + fn get_program_header_table_location(&self, is_32bits: bool) -> Option { + if is_32bits { + self.get_address_point(0x1c, true) + } else { + self.get_address_point(0x20, false) + } + } + + /// Memory address of the start of the section header table + fn get_section_header_table_location(&self, is_32bits: bool) -> Option { + if is_32bits { + self.get_address_point(0x20, true) + } else { + self.get_address_point(0x28, false) + } + } + + /// Return the size of the header, normally, 0x40 for 64 bits bin and 0x34 for 32 bits + fn get_elf_header_size(&self, is_32bits: bool) -> Option { + let address = if is_32bits { 0x28 } else { 0x34 }; + self.get_u16_value(address) + } + + /// return the size of the program header + fn get_program_header_size(&self, is_32bits: bool) -> Option { + let address = if is_32bits { 0x2a } else { 0x34 }; + self.get_u16_value(address) + } + + /// return the number of entries in the program header + fn get_number_entries_program_header(&self, is_32bits: bool) -> Option { + let address = if is_32bits { 0x2c } else { 0x38 }; + self.get_u16_value(address) + } + + /// Return the size of the section header + fn get_section_header_size(&self, is_32bits: bool) -> Option { + let address = if is_32bits { 0x2e } else { 0x3a }; + self.get_u16_value(address) + } + + /// Return the number of entries in the section header + fn get_section_header_num_entries(&self, is_32bits: bool) -> Option { + let address = if is_32bits { 0x30 } else { 0x3c }; + self.get_u16_value(address) + } + + /// Return a u16 value, usually for the size or the number of entries inside a header + fn get_u16_value(&self, address: usize) -> Option { + let mut bytes: [u8; 2] = [0; 2]; + bytes[0] = self.bytes.get(address).copied()?; + bytes[1] = self.bytes.get(address + 1).copied()?; + Option::Some(u16::from_le_bytes(bytes)) + } + + /// return the memory address of something stored at address + /// Can return None if the file is smaller than adress + 4 (or 7 if 64 bits), in this case, the elf header is incorrect + fn get_address_point(&self, address: usize, is_32bits: bool) -> Option { + if is_32bits { + let mut bytes: [u8; 4] = [0; 4]; + bytes[0] = self.bytes.get(address).copied()?; + bytes[0] = self.bytes.get(address + 1).copied()?; + bytes[0] = self.bytes.get(address + 2).copied()?; + bytes[0] = self.bytes.get(address + 3).copied()?; + Option::Some(u32::from_le_bytes(bytes) as u64) + } else { + let mut bytes: [u8; 8] = [0; 8]; + bytes[0] = self.bytes.get(address).copied()?; + bytes[0] = self.bytes.get(address + 1).copied()?; + bytes[0] = self.bytes.get(address + 2).copied()?; + bytes[0] = self.bytes.get(address + 3).copied()?; + bytes[0] = self.bytes.get(address + 4).copied()?; + bytes[0] = self.bytes.get(address + 5).copied()?; + bytes[0] = self.bytes.get(address + 6).copied()?; + bytes[0] = self.bytes.get(address + 7).copied()?; + Option::Some(u64::from_le_bytes(bytes)) + } + } + + +} + +#[cfg(test)] +mod test { + use crate::simulator::{loader::Loader, machine::Machine}; + + + #[test] + fn test_parse() { + let mut machine = Machine::init_machine(); + let loader = Loader::load("./test/riscv_instructions/simple_arithmetics/unsigned_addition", &mut machine, 0).expect("IO Error"); + assert_eq!(true, loader.is_elf()); + assert_eq!(false, loader.is_32bits()); + assert_eq!(false, loader.check_endianess()); + } + } \ No newline at end of file