Implement read support for intel hex files, some ObjectFileReader refactoring

This commit is contained in:
Keenan Tims 2023-11-04 15:17:52 -07:00
parent 51ad57bdaf
commit a3a5d61838
Signed by: ktims
GPG Key ID: 11230674D69038D4
2 changed files with 72 additions and 51 deletions

View File

@ -2,6 +2,7 @@ extern crate object;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::iter::repeat;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -29,7 +30,9 @@ struct IhexWriter;
struct BinWriter; struct BinWriter;
struct ElfReader; struct ElfReader;
struct IhexReader; struct IhexReader {
file_contents: String,
}
struct BinReader { struct BinReader {
filesize: usize, filesize: usize,
file: File, file: File,
@ -47,12 +50,12 @@ impl MemoryFileType {
_ => None, _ => None,
} }
} }
pub fn read_object_file( pub fn object_file_reader(
filename: &PathBuf, filename: &PathBuf,
) -> Result<Box<dyn ObjectFileReader>, std::io::Error> { ) -> Result<Box<dyn ObjectFileReader>, std::io::Error> {
match MemoryFileType::type_from_filename(filename.to_str().unwrap_or("")) { match MemoryFileType::type_from_filename(filename.to_str().unwrap_or("")) {
Some(MemoryFileType::Elf) => unimplemented!(), Some(MemoryFileType::Elf) => unimplemented!(),
Some(MemoryFileType::Ihex) => unimplemented!(), Some(MemoryFileType::Ihex) => IhexReader::new(filename),
Some(MemoryFileType::Bin) => BinReader::new(filename), Some(MemoryFileType::Bin) => BinReader::new(filename),
None => { None => {
warn!("Can't determine an appropriate output type based on filename {}, reading as raw binary", filename.to_str().unwrap_or("<invalid utf-8>")); warn!("Can't determine an appropriate output type based on filename {}, reading as raw binary", filename.to_str().unwrap_or("<invalid utf-8>"));
@ -85,21 +88,10 @@ pub trait ObjectFileReader {
fn new(file: &PathBuf) -> Result<Box<dyn ObjectFileReader>, std::io::Error> fn new(file: &PathBuf) -> Result<Box<dyn ObjectFileReader>, std::io::Error>
where where
Self: Sized; Self: Sized;
fn len(&self) -> usize; /// Return the base address and entire data of `text` sections in the object file,
/// Set buf's contents to the object file's first `text` section, up to `size` bytes /// copied into a Vec<u8>. This will allocate the necessary space for the entire memory
fn read_first( /// map of the `text` sections, which in some cases is probably a horrible idea.
&mut self, fn read_all(&mut self, size: Option<u32>) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
buf: &mut Vec<u8>,
size: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>>;
/// 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<u8>,
addr: u32,
size: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>>;
} }
/// An object that can write a block of memory to a file /// 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(); let filesize = file.metadata()?.len().try_into().unwrap();
Ok(Box::new(BinReader { file, filesize })) Ok(Box::new(BinReader { file, filesize }))
} }
fn len(&self) -> usize {
self.filesize
}
fn read_at(
&mut self,
buf: &mut Vec<u8>,
addr: u32,
size: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>> {
if addr as usize > self.filesize {
return Err("Can't read past the end of the file!".into());
}
let size = std::cmp::min( fn read_all(&mut self, size: Option<u32>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
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<u8>,
size: Option<u32>,
) -> Result<(), Box<dyn std::error::Error>> {
// The casting here is a bit janky... // The casting here is a bit janky...
let size = std::cmp::min(size.unwrap_or(self.filesize as u32) as usize, self.filesize); let size = std::cmp::min(size.unwrap_or(self.filesize as u32) as usize, self.filesize);
buf.resize(size as usize, 0); let mut buf = Vec::from_iter(std::iter::repeat(0).take(size));
Ok(self.file.read_exact(buf)?) self.file.read_exact(&mut buf)?;
Ok(buf)
}
}
impl ObjectFileReader for IhexReader {
fn new(file: &PathBuf) -> Result<Box<dyn ObjectFileReader>, 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<u32>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
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)
} }
} }

View File

@ -117,12 +117,9 @@ impl ObjectFileType {
Self::Elf => MemoryFileType::Elf.mem_writer(), Self::Elf => MemoryFileType::Elf.mem_writer(),
} }
} }
fn file_reader( fn file_reader(&self, filename: &PathBuf) -> Result<Box<dyn ObjectFileReader>, std::io::Error> {
&self,
filename: &PathBuf,
) -> Result<Box<dyn ObjectFileReader>, std::io::Error> {
match self { match self {
Self::Auto => MemoryFileType::read_object_file(filename), Self::Auto => MemoryFileType::object_file_reader(filename),
_ => unimplemented!(), _ => unimplemented!(),
} }
} }