From 51ad57bdaf471c16015b19af2e025870ad3507ad Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Fri, 3 Nov 2023 20:08:01 -0700 Subject: [PATCH] Work on object file interface. Add ability to write ELF files that works. Set up trait and basic reader for bin files. --- src/file.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++------- src/main.rs | 25 +++++++---- 2 files changed, 127 insertions(+), 25 deletions(-) diff --git a/src/file.rs b/src/file.rs index 5504945..e09d5b7 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,5 +1,10 @@ extern crate object; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; +use std::path::PathBuf; + use clap::ValueEnum; use log::debug; use log::warn; @@ -23,8 +28,38 @@ struct ElfWriter; struct IhexWriter; struct BinWriter; +struct ElfReader; +struct IhexReader; +struct BinReader { + filesize: usize, + file: File, +} + /// Build a new object to manage writing type T impl MemoryFileType { + fn type_from_filename(filename: &str) -> Option { + let filename = filename.to_lowercase(); + let (_, extension) = filename.rsplit_once('.').unwrap_or(("", "")); + match extension { + "elf" => Some(MemoryFileType::Elf), + "hex" | "ihex" => Some(MemoryFileType::Ihex), + "bin" | "raw" => Some(MemoryFileType::Bin), + _ => None, + } + } + pub fn read_object_file( + 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::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("")); + unimplemented!() + } + } + } pub fn mem_writer(&self) -> Box { match *self { MemoryFileType::Elf => Box::new(ElfWriter), @@ -32,21 +67,41 @@ impl MemoryFileType { MemoryFileType::Bin => Box::new(BinWriter), } } - pub fn mem_writer_from_filename(filename: &str) -> Box { - let filename = filename.to_lowercase(); - let (_, extension) = filename.rsplit_once('.').unwrap_or(("", "")); - match extension { - "elf" => Box::new(ElfWriter), - "hex" | "ihex" => Box::new(IhexWriter), - "bin" | "raw" => Box::new(BinWriter), - _ => { - warn!("Can't determine an appropriate output type based on filename {}, writing as raw binary", filename); + pub fn mem_writer_for_file(filename: &PathBuf) -> Box { + match MemoryFileType::type_from_filename(filename.to_str().unwrap_or("")) { + Some(MemoryFileType::Elf) => Box::new(ElfWriter), + Some(MemoryFileType::Ihex) => Box::new(IhexWriter), + Some(MemoryFileType::Bin) => Box::new(BinWriter), + None => { + warn!("Can't determine an appropriate output type based on filename {}, writing as raw binary", filename.to_str().unwrap_or("")); Box::new(BinWriter) } } } } +/// An object that can read a block of memory from an object file +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>; +} + /// An object that can write a block of memory to a file pub trait MemoryWriter { fn write_mem( @@ -57,8 +112,6 @@ pub trait MemoryWriter { ) -> Result<(), Box>; } -// object is not well documented, see how this API must be used here: -// https://github.com/gimli-rs/object/blob/master/src/write/elf/object.rs#L217 impl MemoryWriter for ElfWriter { fn write_mem( &mut self, @@ -69,6 +122,9 @@ impl MemoryWriter for ElfWriter { debug!("Writing as ELF with base address 0x{:x}", addr); let mut buf = Vec::new(); + // object is not well documented, see how this API must be used here: + // https://github.com/gimli-rs/object/blob/master/src/write/elf/object.rs#L217 + let mut writer = object::write::elf::Writer::new(Endianness::Little, false, &mut buf); // Calculating offsets @@ -99,12 +155,6 @@ impl MemoryWriter for ElfWriter { e_machine: elf::EM_ARM, e_entry: addr.into(), e_flags: M33_FLAGS, - // e_ident: elf::Ident, - // e_type: elf::ET_EXEC, - // e_entry: addr, - // e_flags: elf::EF_ARM_ABI_FLOAT_HARD | elf::EF_ARM_EABI_VER5, - // e_machine: elf::EM_ARM, - // e_version: elf::EV_CURRENT, })?; writer.write_align(4); writer.write(&data); @@ -192,3 +242,46 @@ impl MemoryWriter for BinWriter { Ok(output.write_all(data)?) } } + +impl ObjectFileReader for BinReader { + fn new(file: &PathBuf) -> Result, std::io::Error> + where + Self: Sized, + { + let file = File::open(file)?; + 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> { + // 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)?) + } +} diff --git a/src/main.rs b/src/main.rs index b92f8cd..1973090 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use std::{ use anyhow::{anyhow, Context, Error}; use clap_num::maybe_hex; use env_logger::Env; +use file::ObjectFileReader; use log::{debug, error, info, log_enabled, warn, Level}; use hidapi::{DeviceInfo, HidApi}; @@ -108,14 +109,23 @@ enum ObjectFileType { } impl ObjectFileType { - fn mem_writer(&self, filename: &str) -> Box { + fn mem_writer(&self, filename: &PathBuf) -> Box { match self { - Self::Auto => MemoryFileType::mem_writer_from_filename(filename), + Self::Auto => MemoryFileType::mem_writer_for_file(filename), Self::Bin => MemoryFileType::Bin.mem_writer(), Self::Ihex => MemoryFileType::Ihex.mem_writer(), Self::Elf => MemoryFileType::Elf.mem_writer(), } } + fn file_reader( + &self, + filename: &PathBuf, + ) -> Result, std::io::Error> { + match self { + Self::Auto => MemoryFileType::read_object_file(filename), + _ => unimplemented!(), + } + } } #[derive(Args, Debug)] @@ -139,6 +149,8 @@ struct ReadArgs { #[derive(Args, Debug)] struct WriteArgs { file: PathBuf, + #[arg(short = 't', long = "type", value_enum, default_value_t = ObjectFileType::Auto)] + filetype: ObjectFileType, /// USB device to act on #[command(flatten)] @@ -212,12 +224,9 @@ fn write_file_to_flash(args: &WriteArgs) -> Result<(), Error> { let api = HidApi::new()?; let isp = connect_device(&api, &args.devspec)?; - let infile = fs::read(args.file.as_path()) + let infile = args.filetype.file_reader(&args.file) .with_context(|| format!("Opening {} for reading", args.file.display()))?; - let in_obj = object::File::parse(&*infile) - .with_context(|| format!("Parsing {} as a binary object", args.file.display()))?; - println!("{:?}", in_obj.sections()); let flash_start = args.addr.unwrap_or(isp.GetFlashStartAddress()?); let flash_size = args.size.unwrap_or(isp.GetFlashSizeInBytes()?); @@ -267,7 +276,7 @@ fn read_flash_to_file(args: &ReadArgs) -> Result<(), Error> { } }; - let mut writer = args.filetype.mem_writer(args.file.to_str().unwrap_or("")); + let mut writer = args.filetype.mem_writer(&args.file); let write_pb: ProgressBar = ProgressBar::new(buf.len() as u64) .with_style(read_write_style()) @@ -281,7 +290,7 @@ fn read_flash_to_file(args: &ReadArgs) -> Result<(), Error> { .write_mem(&mut write_pb.wrap_write(output), flash_start, &buf) .or_else(|e| Err(anyhow!("Error writing to object file: {}", e)))?; write_pb.finish_using_style(); - + Ok(()) }