Continue to add comments

This commit is contained in:
Quentin Legot 2023-04-03 15:58:57 +02:00
parent 025ede6080
commit aef8d219d0

View File

@ -129,7 +129,7 @@ impl ElfHeader {
/// memory address of the entry point from where the process starts its execution /// memory address of the entry point from where the process starts its execution
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// Memory address of the start of the program header table
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// Memory address of the start of the section header table
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// 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 /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// return the size of a program header table entry
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// return the number of entries in the program header
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// Return the size of a section header table entry
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// Return the number of entries in the section header
/// ///
/// ## Arguments: /// ## Paramters:
/// ///
/// **instructions** List of bytes of the loaded binary file /// **instructions** List of bytes of the loaded binary file
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits /// **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 /// 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 /// **instructions** List of bytes of the loaded binary file
/// **address** Position of the first byte /// **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) get_address_point(instructions, address + if is_32bits { 0x0C } else { 0x10 }, is_32bits)
} }
fn get_image_offset(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u64> { /// Return the offset of the section in the file image (binary file)
fn get_image_offset(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
get_address_point(instructions, address + if is_32bits { 0x10 } else { 0x18 }, is_32bits) get_address_point(instructions, address + if is_32bits { 0x10 } else { 0x18 }, is_32bits)
} }
fn get_section_size(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u64> { /// 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<u64> {
get_address_point(instructions, address + if is_32bits { 0x14 } else { 0x20 }, is_32bits) get_address_point(instructions, address + if is_32bits { 0x14 } else { 0x20 }, is_32bits)
} }
fn get_section_link(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u32> { fn get_section_link(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
get_address_point(instructions, address + if is_32bits { 0x18 } else { 0x28 }, false).map(|v| { v as u32 }) get_address_point(instructions, address + if is_32bits { 0x18 } else { 0x28 }, false).map(|v| { v as u32 })
} }
fn get_section_info(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u32> { fn get_section_info(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
get_address_point(instructions, address + if is_32bits { 0x1C } else { 0x2C }, false).map(|v| { v as u32 }) get_address_point(instructions, address + if is_32bits { 0x1C } else { 0x2C }, false).map(|v| { v as u32 })
} }
fn get_required_align(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u64> { /// 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<u64> {
get_address_point(instructions, address + if is_32bits { 0x20 } else { 0x30 }, is_32bits) get_address_point(instructions, address + if is_32bits { 0x20 } else { 0x30 }, is_32bits)
} }
fn get_entry_size(instructions: &Vec<u8>, address: usize, is_32bits: bool) -> Option<u64> { /// 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<u64> {
get_address_point(instructions, address + if is_32bits { 0x24 } else { 0x38 }, is_32bits) get_address_point(instructions, address + if is_32bits { 0x24 } else { 0x38 }, is_32bits)
} }
} }
impl TryFrom<(&Vec<u8>, u64, bool)> for SectionHeader { impl TryFrom<(&[u8], u64, bool)> for SectionHeader {
type Error = (); type Error = ();
fn try_from(value: (&Vec<u8>, u64, bool)) -> Result<Self, Self::Error> { fn try_from(value: (&[u8], u64, bool)) -> Result<Self, Self::Error> {
let instructions = value.0; let instructions = value.0;
let address = value.1 as usize; let address = value.1 as usize;
let is_32bits = value.2; let is_32bits = value.2;
@ -428,26 +432,47 @@ impl TryFrom<(&Vec<u8>, u64, bool)> for SectionHeader {
} }
} }
pub struct Loader { /// Error enum for [`Loader`]
bytes: Vec<u8>,
pub elf_header: ElfHeader,
pub sections: Vec<SectionHeader>
}
#[derive(Debug)] #[derive(Debug)]
pub enum LoaderError { pub enum LoaderError {
/// Correspond to std IO error
IOError(std::io::Error), IOError(std::io::Error),
/// Others errors
ParsingError ParsingError
} }
/// Global structure of the loader, one instance per loaded files
pub struct Loader {
/// List of bytes inside the binary file
bytes: Vec<u8>,
/// Elf header, see [`ElfHeader`] for more informations
pub elf_header: ElfHeader,
/// Section header table entries, see [`SectionHeader`] for more informations
pub sections: Vec<SectionHeader>
}
impl Loader { 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> { pub fn new(path: &str, machine: &mut Machine, start_index: usize) -> Result<(Self, u64), LoaderError> {
let loader = Self::load_and_parse(path)?; let loader = Self::load_and_parse(path)?;
let end_alloc = loader.load_into_machine(machine, start_index)?; let end_alloc = loader.load_into_machine(machine, start_index)?;
Ok((loader, end_alloc)) 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<u64, LoaderError> { fn load_into_machine(&self, machine: &mut Machine, start_index: usize) -> Result<u64, LoaderError> {
let mut end_index = 0; let mut end_index = 0;
for i in 0..self.sections.len() { for i in 0..self.sections.len() {
@ -457,7 +482,8 @@ impl Loader {
// Can allocate to machine memory // Can allocate to machine memory
for j in (0..section.section_size as usize).step_by(4) { for j in (0..section.section_size as usize).step_by(4) {
let mut buf: [u8; 4] = [0; 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)?; 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); 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) 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<Self, LoaderError> { fn load_and_parse(path: &str) -> Result<Self, LoaderError> {
let file = fs::File::open(path); let file = fs::File::open(path);
match file { match file {
@ -507,15 +535,27 @@ impl Loader {
}; };
// #[cfg(debug_assertions)] // #[cfg(debug_assertions)]
// println!("{:04x?}", instructions); // only print loaded program in debug build // 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) => { Err(err) => {
return Err(LoaderError::IOError(err)); Err(LoaderError::IOError(err))
} }
}; }
} }
fn parse_section_header(instructions: &Vec<u8>, is_32bits: bool, header_location: u64, num_of_entries: u16, entry_size: u16) -> Result<Vec<SectionHeader>, ()> {
/// 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<Vec<SectionHeader>, ()> {
let mut sections: Vec<SectionHeader> = Default::default(); let mut sections: Vec<SectionHeader> = Default::default();
for i in 0..num_of_entries as u64 { 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)?); sections.push(Self::parse_section_entry(instructions, is_32bits, header_location + i * entry_size as u64)?);
@ -523,7 +563,15 @@ impl Loader {
Ok(sections) Ok(sections)
} }
fn parse_section_entry(instructions: &Vec<u8>, is_32bits: bool, location: u64) -> Result<SectionHeader, ()> {
/// 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, ()> {
SectionHeader::try_from((instructions, location, is_32bits)) SectionHeader::try_from((instructions, location, is_32bits))
} }