From 443556b18a4753a99822cf1beb0661784a7be367 Mon Sep 17 00:00:00 2001 From: Quentin Legot Date: Fri, 31 Mar 2023 21:49:26 +0200 Subject: [PATCH] Refactor loader into multiple struct --- src/simulator/loader.rs | 321 ++++++++++++++++++++++++++++------------ 1 file changed, 224 insertions(+), 97 deletions(-) diff --git a/src/simulator/loader.rs b/src/simulator/loader.rs index 2eac03c..1a968c8 100644 --- a/src/simulator/loader.rs +++ b/src/simulator/loader.rs @@ -1,9 +1,6 @@ use crate::Machine; - -use std::f32::consts::E; +use std::fmt::Error; use std::fs; -use std::io; -use std::io::BufRead; use std::io::Read; /// load a 32-bits binary file into the machine @@ -35,154 +32,279 @@ pub fn load(path: &str, machine: &mut Machine, start_index: usize) -> Result<(), Ok(()) } -struct Loader { - bytes: Vec +pub struct ElfHeader { + pub endianess: bool, + pub is_32bits: bool, + pub version: u8, + pub sys_v_abi: bool, + pub is_riscv_target: bool, + pub entrypoint: u64, + pub elf_header_size: u16, + pub program_header_location: u64, + pub program_header_entries: u16, + pub program_header_size: u16, + pub section_header_location: u64, + pub section_header_entries: u16, + pub section_header_size: u16, } -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(()) - } - +impl ElfHeader { + /// 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]) + fn is_elf(instructions: &Vec) -> bool { + instructions.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) + fn check_endianess(instructions: &Vec) -> bool { + instructions.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) + fn is_32bits(instructions: &Vec) -> bool { + instructions.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 + fn get_version(instructions: &Vec) -> Option { + instructions.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) + fn is_system_v_elf(instructions: &Vec) -> bool { + instructions.get(7) == Option::Some(&0) } /// return true if specified target instruction set architecture is RISCV - fn is_riscv_isa(&self) -> bool { - self.get_u16_value(0x12) == Option::Some(0xf3) + fn is_riscv_isa(instructions: &Vec) -> bool { + Self::get_u16_value(instructions, 0x12) == Option::Some(0xf3) } /// memory address of the entry point from where the process starts its execution - fn get_entrypoint(&self, is_32bits: bool) -> Option { + fn get_entrypoint(instructions: &Vec, is_32bits: bool) -> Option { if is_32bits { - self.get_address_point(0x18, true) + Self::get_address_point(instructions, 0x18, true) } else { - self.get_address_point(0x18, false) + Self::get_address_point(instructions, 0x18, false) } } /// Memory address of the start of the program header table - fn get_program_header_table_location(&self, is_32bits: bool) -> Option { + fn get_program_header_table_location(instructions: &Vec, is_32bits: bool) -> Option { if is_32bits { - self.get_address_point(0x1c, true) + Self::get_address_point(instructions, 0x1c, true) } else { - self.get_address_point(0x20, false) + Self::get_address_point(instructions, 0x20, false) } } /// Memory address of the start of the section header table - fn get_section_header_table_location(&self, is_32bits: bool) -> Option { + fn get_section_header_table_location(instructions: &Vec, is_32bits: bool) -> Option { if is_32bits { - self.get_address_point(0x20, true) + Self::get_address_point(instructions, 0x20, true) } else { - self.get_address_point(0x28, false) + Self::get_address_point(instructions, 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 { + fn get_elf_header_size(instructions: &Vec, is_32bits: bool) -> Option { let address = if is_32bits { 0x28 } else { 0x34 }; - self.get_u16_value(address) + Self::get_u16_value(instructions, 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 size of a program header table entry + fn get_program_header_size(instructions: &Vec, is_32bits: bool) -> Option { + let address = if is_32bits { 0x2a } else { 0x36 }; + Self::get_u16_value(instructions, address) } /// return the number of entries in the program header - fn get_number_entries_program_header(&self, is_32bits: bool) -> Option { + fn get_number_entries_program_header(instructions: &Vec, is_32bits: bool) -> Option { let address = if is_32bits { 0x2c } else { 0x38 }; - self.get_u16_value(address) + Self::get_u16_value(instructions, address) } - /// Return the size of the section header - fn get_section_header_size(&self, is_32bits: bool) -> Option { + /// Return the size of a section header table entry + fn get_section_header_size(instructions: &Vec, is_32bits: bool) -> Option { let address = if is_32bits { 0x2e } else { 0x3a }; - self.get_u16_value(address) + Self::get_u16_value(instructions, address) } /// Return the number of entries in the section header - fn get_section_header_num_entries(&self, is_32bits: bool) -> Option { + fn get_section_header_num_entries(instructions: &Vec, 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)) + Self::get_u16_value(instructions, address) } /// return the memory address of something stored at address /// Can return None if the file is smaller than adress + 3 (or 7 if 64 bits), in this case, the elf header is incorrect - fn get_address_point(&self, address: usize, is_32bits: bool) -> Option { + fn get_address_point(instructions: &Vec, address: usize, is_32bits: bool) -> Option { if is_32bits { let mut bytes: [u8; 4] = [0; 4]; - bytes[0] = self.bytes.get(address).copied()?; - bytes[1] = self.bytes.get(address + 1).copied()?; - bytes[2] = self.bytes.get(address + 2).copied()?; - bytes[3] = self.bytes.get(address + 3).copied()?; + bytes[0] = instructions.get(address).copied()?; + bytes[1] = instructions.get(address + 1).copied()?; + bytes[2] = instructions.get(address + 2).copied()?; + bytes[3] = instructions.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[1] = self.bytes.get(address + 1).copied()?; - bytes[2] = self.bytes.get(address + 2).copied()?; - bytes[3] = self.bytes.get(address + 3).copied()?; - bytes[4] = self.bytes.get(address + 4).copied()?; - bytes[5] = self.bytes.get(address + 5).copied()?; - bytes[6] = self.bytes.get(address + 6).copied()?; - bytes[7] = self.bytes.get(address + 7).copied()?; + bytes[0] = instructions.get(address).copied()?; + bytes[1] = instructions.get(address + 1).copied()?; + bytes[2] = instructions.get(address + 2).copied()?; + bytes[3] = instructions.get(address + 3).copied()?; + bytes[4] = instructions.get(address + 4).copied()?; + bytes[5] = instructions.get(address + 5).copied()?; + bytes[6] = instructions.get(address + 6).copied()?; + bytes[7] = instructions.get(address + 7).copied()?; Option::Some(u64::from_le_bytes(bytes)) } } + /// Return a u16 value, usually for the size or the number of entries inside a header + fn get_u16_value(instructions: &Vec, address: usize) -> Option { + let mut bytes: [u8; 2] = [0; 2]; + bytes[0] = instructions.get(address).copied()?; + bytes[1] = instructions.get(address + 1).copied()?; + Option::Some(u16::from_le_bytes(bytes)) + } + +} + +impl TryFrom<&Vec> for ElfHeader { + type Error = (); + + fn try_from(instructions: &Vec) -> Result { + if Self::is_elf(instructions) { + let format = Self::is_32bits(instructions); + let endianess = Self::check_endianess(instructions); + let version = Self::get_version(instructions).ok_or(())?; + let is_sys_v_abi = Self::is_system_v_elf(instructions); + let is_rv_target = Self::is_riscv_isa(instructions); + let entrypoint = Self::get_entrypoint(instructions, format).ok_or(())?; + let elf_header_size = Self::get_elf_header_size(instructions, format).ok_or(())?; + let program_header_location = Self::get_program_header_table_location(instructions, format).ok_or(())?; + let program_header_entries = Self::get_number_entries_program_header(instructions, format).ok_or(())? ; + let program_header_size = Self::get_program_header_size(instructions, format).ok_or(())?; + let section_header_location = Self::get_section_header_table_location(instructions, format).ok_or(())?; + let section_header_entries = Self::get_section_header_num_entries(instructions, format).ok_or(())?; + let section_header_size = Self::get_section_header_size(instructions, format).ok_or(())?; + Ok(ElfHeader { + endianess, + is_32bits: format, + version, + sys_v_abi: is_sys_v_abi, + is_riscv_target: is_rv_target, + entrypoint, + elf_header_size, + program_header_location, + program_header_entries, + program_header_size, + section_header_location, + section_header_entries, + section_header_size + }) + } else { + Err(()) + } + } +} + +pub struct SectionHeader { + pub name_offset: u32, + pub header_type: u32, + pub attribute: u64, + pub virt_addr: u64, + pub image_offset: u64, + pub section_size: u64, + pub section_index: u32, + pub section_info: u32, + pub required_align: u64, + pub entry_size: u64 +} + +impl TryFrom<(&Vec, u64)> for SectionHeader { + type Error = (); + + fn try_from(value: (&Vec, u64)) -> Result { + todo!() + } +} + +pub struct Loader { + bytes: Vec, + pub elf_header: ElfHeader, + pub sections: Vec +} + +pub enum LoaderError { + IOError(std::io::Error), + ParsingError +} + +impl Loader { + + pub fn load(path: &str, machine: &mut Machine, start_index: usize) -> Result { + let file = fs::File::open(path); + match file { + Ok(mut file) => { + let mut instructions: Vec = Default::default(); + loop { + let mut buf: [u8; 1] = [0; 1]; + let res = file.read(&mut buf); + match res { + Ok(res) => { + if res == 0 { + break; // eof + } else { + instructions.push(buf[0]); + } + }, + Err(err) => { + return Err(LoaderError::IOError(err)) + } + } + + } + let elf_header = match ElfHeader::try_from(&instructions) { + Ok(header) => { + header + }, + Err(_) => { + return Err(LoaderError::ParsingError); + } + }; + let section_header = match Self::parse_section_header(&instructions, elf_header.is_32bits, elf_header.section_header_location, elf_header.section_header_entries, elf_header.section_header_size) { + Ok(header) => { + header + }, + Err(_) => { + return Err(LoaderError::ParsingError); + } + }; + // #[cfg(debug_assertions)] + // println!("{:04x?}", instructions); // only print loaded program in debug build + return Ok(Self { bytes: instructions, elf_header, sections: section_header }); + }, + Err(err) => { + return Err(LoaderError::IOError(err)); + } + }; + } + + fn parse_section_header(instructions: &Vec, is_32bits: bool, header_location: u64, num_of_entries: u16, entry_size: u16) -> Result, ()> { + let mut sections: Vec = Default::default(); + for i in 0..num_of_entries as u64 { + sections.push(Self::parse_section_entry(instructions, is_32bits, header_location + i * entry_size as u64)?); + } + Ok(sections) + } + + fn parse_section_entry(instructions: &Vec, is_32bits: bool, location: u64) -> Result { + SectionHeader::try_from((instructions, location)) + } + } @@ -192,18 +314,23 @@ mod test { #[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()); - assert_eq!(true, loader.is_system_v_elf()); - assert_eq!(true, loader.is_riscv_isa()); - assert_eq!(Option::Some(1), loader.get_version()); - assert_eq!(Option::Some(0x4000), loader.get_entrypoint(false)); - assert_eq!(Option::Some(64), loader.get_program_header_table_location(false)); - assert_eq!(Option::Some(18984), loader.get_section_header_table_location(false)); + fn test_parse_elf() { + // 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()); + // assert_eq!(true, loader.is_system_v_elf()); + // assert_eq!(true, loader.is_riscv_isa()); + // assert_eq!(Option::Some(1), loader.get_version()); + // assert_eq!(Option::Some(0x4000), loader.get_entrypoint(false)); + // assert_eq!(Option::Some(64), loader.get_elf_header_size(false)); + // assert_eq!(Option::Some(64), loader.get_program_header_table_location(false)); + // assert_eq!(Option::Some(18984), loader.get_section_header_table_location(false)); + // assert_eq!(Option::Some(56), loader.get_program_header_size(false)); + // assert_eq!(Option::Some(64), loader.get_section_header_size(false)); + // assert_eq!(Option::Some(4), loader.get_number_entries_program_header(false)); + // assert_eq!(Option::Some(9), loader.get_section_header_num_entries(false)); } } \ No newline at end of file