diff --git a/src/file.rs b/src/file.rs index e09d5b7..d6b3bd4 100644 --- a/src/file.rs +++ b/src/file.rs @@ -2,6 +2,7 @@ extern crate object; use std::fs::File; use std::io::prelude::*; +use std::iter::repeat; use std::path::Path; use std::path::PathBuf; @@ -29,7 +30,9 @@ struct IhexWriter; struct BinWriter; struct ElfReader; -struct IhexReader; +struct IhexReader { + file_contents: String, +} struct BinReader { filesize: usize, file: File, @@ -47,12 +50,12 @@ impl MemoryFileType { _ => None, } } - pub fn read_object_file( + pub fn object_file_reader( filename: &PathBuf, ) -> Result, std::io::Error> { match MemoryFileType::type_from_filename(filename.to_str().unwrap_or("")) { Some(MemoryFileType::Elf) => unimplemented!(), - Some(MemoryFileType::Ihex) => unimplemented!(), + Some(MemoryFileType::Ihex) => IhexReader::new(filename), Some(MemoryFileType::Bin) => BinReader::new(filename), None => { warn!("Can't determine an appropriate output type based on filename {}, reading as raw binary", filename.to_str().unwrap_or("")); @@ -85,21 +88,10 @@ pub trait ObjectFileReader { fn new(file: &PathBuf) -> Result, std::io::Error> where Self: Sized; - fn len(&self) -> usize; - /// Set buf's contents to the object file's first `text` section, up to `size` bytes - fn read_first( - &mut self, - buf: &mut Vec, - size: Option, - ) -> Result<(), Box>; - /// Set buf's contents to the object file's contents, starting from LMA `addr`` to a maximum of `size` bytes, - /// or to the end of the section containing `addr` - fn read_at( - &mut self, - buf: &mut Vec, - addr: u32, - size: Option, - ) -> Result<(), Box>; + /// Return the base address and entire data of `text` sections in the object file, + /// copied into a Vec. This will allocate the necessary space for the entire memory + /// map of the `text` sections, which in some cases is probably a horrible idea. + fn read_all(&mut self, size: Option) -> Result, Box>; } /// An object that can write a block of memory to a file @@ -252,36 +244,68 @@ impl ObjectFileReader for BinReader { let filesize = file.metadata()?.len().try_into().unwrap(); Ok(Box::new(BinReader { file, filesize })) } - fn len(&self) -> usize { - self.filesize - } - fn read_at( - &mut self, - buf: &mut Vec, - addr: u32, - size: Option, - ) -> Result<(), Box> { - if addr as usize > self.filesize { - return Err("Can't read past the end of the file!".into()); - } - let size = std::cmp::min( - size.unwrap_or(self.filesize as u32 - addr) as usize, - self.filesize as usize - addr as usize, - ); - - self.file.seek(std::io::SeekFrom::Start(addr.into()))?; - buf.resize(size as usize, 0); - Ok(self.file.read_exact(buf)?) - } - fn read_first( - &mut self, - buf: &mut Vec, - size: Option, - ) -> Result<(), Box> { + fn read_all(&mut self, size: Option) -> Result, Box> { // The casting here is a bit janky... let size = std::cmp::min(size.unwrap_or(self.filesize as u32) as usize, self.filesize); - buf.resize(size as usize, 0); - Ok(self.file.read_exact(buf)?) + let mut buf = Vec::from_iter(std::iter::repeat(0).take(size)); + self.file.read_exact(&mut buf)?; + + Ok(buf) + } +} + +impl ObjectFileReader for IhexReader { + fn new(file: &PathBuf) -> Result, std::io::Error> + where + Self: Sized, + { + // Parse the entire file up front, since we need it for len() anyway. Only store the parsed sections. + let file_contents = std::fs::read_to_string(file)?; + + Ok(Box::new(IhexReader { file_contents })) + } + fn read_all(&mut self, size: Option) -> Result, Box> { + let reader = ihex::Reader::new(&self.file_contents); + let mut cur_base = 0; + let mut data = Vec::new(); + for rec in reader { + if let Ok(record) = rec { + match record { + ihex::Record::ExtendedLinearAddress(a) => { + cur_base = (a as u32) << 16; + debug!( + "ihex: Got ExtendedLinearAddress, new base: {:08x}", + (a as u32) << 16 + ); + } + // Entry point address is not useful for a flash tool, but it is emitted by objcopy etc. so we need to support/ignore it + ihex::Record::StartSegmentAddress { .. } + | ihex::Record::StartLinearAddress(_) => (), + + ihex::Record::Data { offset, mut value } => { + let record_base = cur_base + (offset as u32); + if (data.len() as u32) < record_base { + debug!("Extending vec by {} to take up slack space", (record_base as usize) - data.len()); + data.extend(repeat(0xff).take((record_base as usize) - data.len())); + } + data.append(&mut value); + } + ihex::Record::EndOfFile => continue, + _ => unimplemented!( + "Only I32HEX format files are supported. We don't support record type {:?}", + record + ), + } + } else { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidData, + rec.unwrap_err().to_string(), + ) + .into()); + } + } + + Ok(data) } } diff --git a/src/main.rs b/src/main.rs index 1973090..7580a6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -117,12 +117,9 @@ impl ObjectFileType { Self::Elf => MemoryFileType::Elf.mem_writer(), } } - fn file_reader( - &self, - filename: &PathBuf, - ) -> Result, std::io::Error> { + fn file_reader(&self, filename: &PathBuf) -> Result, std::io::Error> { match self { - Self::Auto => MemoryFileType::read_object_file(filename), + Self::Auto => MemoryFileType::object_file_reader(filename), _ => unimplemented!(), } }