diff --git a/src/simulator/loader.rs b/src/simulator/loader.rs index 3952013..82be5e7 100644 --- a/src/simulator/loader.rs +++ b/src/simulator/loader.rs @@ -129,7 +129,7 @@ impl ElfHeader { /// memory address of the entry point from where the process starts its execution /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -143,7 +143,7 @@ impl ElfHeader { /// Memory address of the start of the program header table /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -157,7 +157,7 @@ impl ElfHeader { /// Memory address of the start of the section header table /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -171,7 +171,7 @@ impl ElfHeader { /// Return the size of the header, normally, 0x40 for 64 bits bin and 0x34 for 32 bits /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -182,7 +182,7 @@ impl ElfHeader { /// return the size of a program header table entry /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -193,7 +193,7 @@ impl ElfHeader { /// return the number of entries in the program header /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -204,7 +204,7 @@ impl ElfHeader { /// Return the size of a section header table entry /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -215,7 +215,7 @@ impl ElfHeader { /// Return the number of entries in the section header /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **is_32bits** defines whether the binary file is 32 bits or 64 bits @@ -228,7 +228,7 @@ impl ElfHeader { /// /// This method retrieve 2 bytes and concatenate them assuming the file is little endian /// - /// ## Arguments: + /// ## Paramters: /// /// **instructions** List of bytes of the loaded binary file /// **address** Position of the first byte @@ -370,36 +370,40 @@ impl SectionHeader { get_address_point(instructions, address + if is_32bits { 0x0C } else { 0x10 }, is_32bits) } - fn get_image_offset(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + /// Return the offset of the section in the file image (binary file) + fn get_image_offset(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x10 } else { 0x18 }, is_32bits) } - fn get_section_size(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + /// Return the size of the section in the file image (binary file), may be 0 + fn get_section_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x14 } else { 0x20 }, is_32bits) } - fn get_section_link(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + fn get_section_link(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x18 } else { 0x28 }, false).map(|v| { v as u32 }) } - fn get_section_info(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + fn get_section_info(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x1C } else { 0x2C }, false).map(|v| { v as u32 }) } - fn get_required_align(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + /// Return the required alignment of the section, must be a power of 2 + fn get_required_align(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x20 } else { 0x30 }, is_32bits) } - fn get_entry_size(instructions: &Vec, address: usize, is_32bits: bool) -> Option { + /// Contain the size of each entry for sections that contain fixed-size entries, otherwise 0 + fn get_entry_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option { get_address_point(instructions, address + if is_32bits { 0x24 } else { 0x38 }, is_32bits) } } -impl TryFrom<(&Vec, u64, bool)> for SectionHeader { +impl TryFrom<(&[u8], u64, bool)> for SectionHeader { type Error = (); - fn try_from(value: (&Vec, u64, bool)) -> Result { + fn try_from(value: (&[u8], u64, bool)) -> Result { let instructions = value.0; let address = value.1 as usize; let is_32bits = value.2; @@ -428,26 +432,47 @@ impl TryFrom<(&Vec, u64, bool)> for SectionHeader { } } -pub struct Loader { - bytes: Vec, - pub elf_header: ElfHeader, - pub sections: Vec -} - +/// Error enum for [`Loader`] #[derive(Debug)] pub enum LoaderError { + /// Correspond to std IO error IOError(std::io::Error), + /// Others errors ParsingError } +/// Global structure of the loader, one instance per loaded files +pub struct Loader { + /// List of bytes inside the binary file + bytes: Vec, + /// Elf header, see [`ElfHeader`] for more informations + pub elf_header: ElfHeader, + /// Section header table entries, see [`SectionHeader`] for more informations + pub sections: Vec +} impl Loader { + /// # Loader constructor + /// + /// Load the binary file given in parameter, parse it and load inside the machine memory + /// return the loader instance and the location of the end of the last a allocated section in memory + /// + /// ## Parameters + /// + /// **path**: location of the binary file on disk + /// **machine**: well, the risc-v simulator + /// **start_index**: The position at which you want to start to allocate the program pub fn new(path: &str, machine: &mut Machine, start_index: usize) -> Result<(Self, u64), LoaderError> { let loader = Self::load_and_parse(path)?; let end_alloc = loader.load_into_machine(machine, start_index)?; Ok((loader, end_alloc)) } + /// Try to load the binary file in memory after it been parsed + /// + /// Binary file is loaded according to sections order and rules, see [`SectionHeader`] + /// + /// Return the location of the end of the last a allocated section in memory fn load_into_machine(&self, machine: &mut Machine, start_index: usize) -> Result { let mut end_index = 0; for i in 0..self.sections.len() { @@ -457,7 +482,8 @@ impl Loader { // Can allocate to machine memory for j in (0..section.section_size as usize).step_by(4) { let mut buf: [u8; 4] = [0; 4]; - for k in 0..4 { + #[allow(clippy::needless_range_loop)] + for k in 0..buf.len() { buf[k] = self.bytes.get(section.image_offset as usize + j + k).copied().ok_or(LoaderError::ParsingError)?; } machine.write_memory(4, start_index + section.virt_addr as usize + j, u32::from_le_bytes(buf) as u64); @@ -467,6 +493,8 @@ impl Loader { Ok(start_index as u64 + end_index) } + /// Load the binary file and store it inside an array and try to parse it, + /// useful for a lot of thing like to know which sections to allocate memory and where fn load_and_parse(path: &str) -> Result { let file = fs::File::open(path); match file { @@ -507,15 +535,27 @@ impl Loader { }; // #[cfg(debug_assertions)] // println!("{:04x?}", instructions); // only print loaded program in debug build - return Ok(Self { bytes: instructions, elf_header, sections: section_header }); + Ok(Self { bytes: instructions, elf_header, sections: section_header }) }, Err(err) => { - return Err(LoaderError::IOError(err)); + Err(LoaderError::IOError(err)) } - }; + } } - fn parse_section_header(instructions: &Vec, is_32bits: bool, header_location: u64, num_of_entries: u16, entry_size: u16) -> Result, ()> { + + /// Try to parse sections header table + /// + /// Create one instance of [`SectionHeader`] for each entry and store it inside an array + /// + /// ## Parameters + /// + /// **instructions**: array of bytes of the binary file + /// **is_32bits**: contain whether the binary file is 32 bits or 64 bits + /// **header_location**: represent the position of the first entry of the header + /// **num_of_entries**: defines the number of section header entries + /// **entry_size**: Defines the size of an entry (each entry have the exact same size), value vary depending of if this binary file is 32 or 64 bits + fn parse_section_header(instructions: &[u8], 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)?); @@ -523,7 +563,15 @@ impl Loader { Ok(sections) } - fn parse_section_entry(instructions: &Vec, is_32bits: bool, location: u64) -> Result { + + /// Parse one entry of the section header + /// + /// ## Parameters: + /// + /// **instructions**: array of bytes of the binary file + /// **is_32bits**: contain whether the binary file is 32 bits or 64 bits + /// **location**: represent the position of the entry on the file image + fn parse_section_entry(instructions: &[u8], is_32bits: bool, location: u64) -> Result { SectionHeader::try_from((instructions, location, is_32bits)) }