From 751ed08606921634ab61558787081aaafcffeb0b Mon Sep 17 00:00:00 2001 From: Nick Spinale Date: Tue, 2 Jul 2024 21:55:57 +0000 Subject: [PATCH] tool: refactor ELF parsing Signed-off-by: Nick Spinale --- tool/microkit/src/elf.rs | 151 ++++++++++++++++++++++++++------------- 1 file changed, 103 insertions(+), 48 deletions(-) diff --git a/tool/microkit/src/elf.rs b/tool/microkit/src/elf.rs index 3aa7a7ab..e4803603 100644 --- a/tool/microkit/src/elf.rs +++ b/tool/microkit/src/elf.rs @@ -10,6 +10,7 @@ use std::collections::HashMap; use crate::util::bytes_to_struct; #[repr(C, packed)] +#[derive(Copy, Clone)] struct ElfHeader32 { ident_magic: u32, ident_class: u8, @@ -45,6 +46,7 @@ struct ElfSymbol64 { } #[repr(C, packed)] +#[derive(Copy, Clone)] struct ElfSectionHeader64 { name: u32, type_: u32, @@ -59,6 +61,7 @@ struct ElfSectionHeader64 { } #[repr(C, packed)] +#[derive(Copy, Clone)] struct ElfProgramHeader64 { type_: u32, flags: u32, @@ -71,6 +74,7 @@ struct ElfProgramHeader64 { } #[repr(C, packed)] +#[derive(Copy, Clone)] struct ElfHeader64 { ident_magic: u32, ident_class: u8, @@ -140,6 +144,64 @@ pub struct ElfFile { impl ElfFile { pub fn from_path(path: &Path) -> Result { + let reader = ElfFileReader::from_path(path)?; + let segments = reader.segments(); + let symbols = reader.symbols()?; + Ok(ElfFile { + word_size: reader.word_size(), + entry: reader.file_header().entry, + segments, + symbols, + }) + } + + pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> { + if let Some((sym, duplicate)) = self.symbols.get(variable_name) { + if *duplicate { + Err(format!("Found multiple symbols with name '{variable_name}'")) + } else { + Ok((sym.value, sym.size)) + } + } else { + Err(format!("No symbol named '{variable_name}' not found")) + } + } + + pub fn write_symbol(&mut self, variable_name: &str, data: &[u8]) -> Result<(), String> { + let (vaddr, size) = self.find_symbol(variable_name)?; + for seg in &mut self.segments { + if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 { + let offset = (vaddr - seg.virt_addr) as usize; + assert!(data.len() as u64 <= size); + seg.data[offset..offset + data.len()].copy_from_slice(data); + return Ok(()); + } + } + + Err(format!("No symbol named {} found", variable_name)) + } + + pub fn get_data(&self, vaddr: u64, size: u64) -> Option<&[u8]> { + for seg in &self.segments { + if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 { + let offset = (vaddr - seg.virt_addr) as usize; + return Some(&seg.data[offset..offset + size as usize]); + } + } + + None + } +} + +struct ElfFileReader<'a> { + path: &'a Path, + bytes: Vec, + word_size: usize, + hdr: ElfHeader64 +} + +impl<'a> ElfFileReader<'a> { + fn from_path(path: &'a Path) -> Result { let bytes = match fs::read(path) { Ok(bytes) => bytes, Err(err) => return Err(format!("Failed to read ELF '{}': {}", path.display(), err)) @@ -172,7 +234,7 @@ impl ElfFile { // Now need to read the header into a struct let hdr_bytes = &bytes[..hdr_size]; - let hdr = unsafe { bytes_to_struct::(hdr_bytes) }; + let hdr = *unsafe { bytes_to_struct::(hdr_bytes) }; // We have checked this above but we should check again once we actually cast it to // a struct. @@ -183,20 +245,44 @@ impl ElfFile { return Err(format!("ELF '{}': incorrect endianness, only little endian architectures are supported", path.display())); } - let entry = hdr.entry; + Ok(Self { + path, + bytes, + word_size, + hdr, + }) + } + + fn path(&self) -> &Path { + self.path + } + + fn bytes(&self) -> &[u8] { + &self.bytes + } + + fn word_size(&self) -> usize { + self.word_size + } + + fn file_header(&self) -> &ElfHeader64 { + &self.hdr + } + + fn segments(&self) -> Vec { + let hdr = self.file_header(); - // Read all the segments let mut segments = Vec::with_capacity(hdr.phnum as usize); for i in 0..hdr.phnum { let phent_start = hdr.phoff + (i * hdr.phentsize) as u64; let phent_end = phent_start + (hdr.phentsize as u64); - let phent_bytes = &bytes[phent_start as usize..phent_end as usize]; + let phent_bytes = &self.bytes()[phent_start as usize..phent_end as usize]; let phent = unsafe { bytes_to_struct::(phent_bytes) }; let segment_start = phent.offset as usize; let segment_end = phent.offset as usize + phent.filesz as usize; - let mut segment_data = Vec::from(&bytes[segment_start..segment_end]); + let mut segment_data = Vec::from(&self.bytes()[segment_start..segment_end]); let num_zeroes = (phent.memsz - phent.filesz) as usize; segment_data.resize(segment_data.len() + num_zeroes, 0); @@ -211,6 +297,12 @@ impl ElfFile { segments.push(segment) } + segments + } + + fn symbols(&self) -> Result, String> { + let hdr = self.file_header(); + // Read all the section headers let mut shents = Vec::with_capacity(hdr.shnum as usize); let mut symtab_shent: Option<&ElfSectionHeader64> = None; @@ -218,7 +310,7 @@ impl ElfFile { for i in 0..hdr.shnum { let shent_start = hdr.shoff + (i * hdr.shentsize) as u64; let shent_end = shent_start + hdr.shentsize as u64; - let shent_bytes = &bytes[shent_start as usize..shent_end as usize]; + let shent_bytes = &self.bytes()[shent_start as usize..shent_end as usize]; let shent = unsafe { bytes_to_struct::(shent_bytes) }; match shent.type_ { @@ -230,23 +322,23 @@ impl ElfFile { } if shstrtab_shent.is_none() { - return Err(format!("ELF '{}': unable to find string table section", path.display())); + return Err(format!("ELF '{}': unable to find string table section", self.path().display())); } assert!(symtab_shent.is_some()); if symtab_shent.is_none() { - return Err(format!("ELF '{}': unable to find symbol table section", path.display())); + return Err(format!("ELF '{}': unable to find symbol table section", self.path().display())); } // Reading the symbol table let symtab_start = symtab_shent.unwrap().offset as usize; let symtab_end = symtab_start + symtab_shent.unwrap().size as usize; - let symtab = &bytes[symtab_start..symtab_end]; + let symtab = &self.bytes()[symtab_start..symtab_end]; let symtab_str_shent = shents[symtab_shent.unwrap().link as usize]; let symtab_str_start = symtab_str_shent.offset as usize; let symtab_str_end = symtab_str_start + symtab_str_shent.size as usize; - let symtab_str = &bytes[symtab_str_start..symtab_str_end]; + let symtab_str = &self.bytes()[symtab_str_start..symtab_str_end]; // Read all the symbols let mut symbols: HashMap = HashMap::new(); @@ -278,44 +370,7 @@ impl ElfFile { offset += symbol_size; } - Ok(ElfFile { word_size, entry, segments, symbols }) - } - - pub fn find_symbol(&self, variable_name: &str) -> Result<(u64, u64), String> { - if let Some((sym, duplicate)) = self.symbols.get(variable_name) { - if *duplicate { - Err(format!("Found multiple symbols with name '{variable_name}'")) - } else { - Ok((sym.value, sym.size)) - } - } else { - Err(format!("No symbol named '{variable_name}' not found")) - } - } - - pub fn write_symbol(&mut self, variable_name: &str, data: &[u8]) -> Result<(), String> { - let (vaddr, size) = self.find_symbol(variable_name)?; - for seg in &mut self.segments { - if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 { - let offset = (vaddr - seg.virt_addr) as usize; - assert!(data.len() as u64 <= size); - seg.data[offset..offset + data.len()].copy_from_slice(data); - return Ok(()); - } - } - - Err(format!("No symbol named {} found", variable_name)) - } - - pub fn get_data(&self, vaddr: u64, size: u64) -> Option<&[u8]> { - for seg in &self.segments { - if vaddr >= seg.virt_addr && vaddr + size <= seg.virt_addr + seg.data.len() as u64 { - let offset = (vaddr - seg.virt_addr) as usize; - return Some(&seg.data[offset..offset + size as usize]); - } - } - - None + Ok(symbols) } fn get_string(strtab: &[u8], idx: usize) -> Result<&str, String> {