Work on object file interface.

Add ability to write ELF files that works. Set up trait and basic reader for bin files.
This commit is contained in:
Keenan Tims 2023-11-03 20:08:01 -07:00
parent ee007da5d4
commit 51ad57bdaf
Signed by: ktims
GPG Key ID: 11230674D69038D4
2 changed files with 127 additions and 25 deletions

View File

@ -1,5 +1,10 @@
extern crate object; extern crate object;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::path::PathBuf;
use clap::ValueEnum; use clap::ValueEnum;
use log::debug; use log::debug;
use log::warn; use log::warn;
@ -23,8 +28,38 @@ struct ElfWriter;
struct IhexWriter; struct IhexWriter;
struct BinWriter; struct BinWriter;
struct ElfReader;
struct IhexReader;
struct BinReader {
filesize: usize,
file: File,
}
/// Build a new object to manage writing type T /// Build a new object to manage writing type T
impl MemoryFileType { impl MemoryFileType {
fn type_from_filename(filename: &str) -> Option<MemoryFileType> {
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<Box<dyn ObjectFileReader>, 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("<invalid utf-8>"));
unimplemented!()
}
}
}
pub fn mem_writer(&self) -> Box<dyn MemoryWriter> { pub fn mem_writer(&self) -> Box<dyn MemoryWriter> {
match *self { match *self {
MemoryFileType::Elf => Box::new(ElfWriter), MemoryFileType::Elf => Box::new(ElfWriter),
@ -32,21 +67,41 @@ impl MemoryFileType {
MemoryFileType::Bin => Box::new(BinWriter), MemoryFileType::Bin => Box::new(BinWriter),
} }
} }
pub fn mem_writer_from_filename(filename: &str) -> Box<dyn MemoryWriter> { pub fn mem_writer_for_file(filename: &PathBuf) -> Box<dyn MemoryWriter> {
let filename = filename.to_lowercase(); match MemoryFileType::type_from_filename(filename.to_str().unwrap_or("")) {
let (_, extension) = filename.rsplit_once('.').unwrap_or(("", "")); Some(MemoryFileType::Elf) => Box::new(ElfWriter),
match extension { Some(MemoryFileType::Ihex) => Box::new(IhexWriter),
"elf" => Box::new(ElfWriter), Some(MemoryFileType::Bin) => Box::new(BinWriter),
"hex" | "ihex" => Box::new(IhexWriter), None => {
"bin" | "raw" => Box::new(BinWriter), warn!("Can't determine an appropriate output type based on filename {}, writing as raw binary", filename.to_str().unwrap_or("<invalid utf-8>"));
_ => {
warn!("Can't determine an appropriate output type based on filename {}, writing as raw binary", filename);
Box::new(BinWriter) Box::new(BinWriter)
} }
} }
} }
} }
/// An object that can read a block of memory from an object file
pub trait ObjectFileReader {
fn new(file: &PathBuf) -> Result<Box<dyn ObjectFileReader>, 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<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
pub trait MemoryWriter { pub trait MemoryWriter {
fn write_mem( fn write_mem(
@ -57,8 +112,6 @@ pub trait MemoryWriter {
) -> Result<(), Box<dyn std::error::Error>>; ) -> Result<(), Box<dyn std::error::Error>>;
} }
// 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 { impl MemoryWriter for ElfWriter {
fn write_mem( fn write_mem(
&mut self, &mut self,
@ -69,6 +122,9 @@ impl MemoryWriter for ElfWriter {
debug!("Writing as ELF with base address 0x{:x}", addr); debug!("Writing as ELF with base address 0x{:x}", addr);
let mut buf = Vec::new(); 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); let mut writer = object::write::elf::Writer::new(Endianness::Little, false, &mut buf);
// Calculating offsets // Calculating offsets
@ -99,12 +155,6 @@ impl MemoryWriter for ElfWriter {
e_machine: elf::EM_ARM, e_machine: elf::EM_ARM,
e_entry: addr.into(), e_entry: addr.into(),
e_flags: M33_FLAGS, 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_align(4);
writer.write(&data); writer.write(&data);
@ -192,3 +242,46 @@ impl MemoryWriter for BinWriter {
Ok(output.write_all(data)?) Ok(output.write_all(data)?)
} }
} }
impl ObjectFileReader for BinReader {
fn new(file: &PathBuf) -> Result<Box<dyn ObjectFileReader>, 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<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(
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...
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)?)
}
}

View File

@ -11,6 +11,7 @@ use std::{
use anyhow::{anyhow, Context, Error}; use anyhow::{anyhow, Context, Error};
use clap_num::maybe_hex; use clap_num::maybe_hex;
use env_logger::Env; use env_logger::Env;
use file::ObjectFileReader;
use log::{debug, error, info, log_enabled, warn, Level}; use log::{debug, error, info, log_enabled, warn, Level};
use hidapi::{DeviceInfo, HidApi}; use hidapi::{DeviceInfo, HidApi};
@ -108,14 +109,23 @@ enum ObjectFileType {
} }
impl ObjectFileType { impl ObjectFileType {
fn mem_writer(&self, filename: &str) -> Box<dyn MemoryWriter> { fn mem_writer(&self, filename: &PathBuf) -> Box<dyn MemoryWriter> {
match self { 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::Bin => MemoryFileType::Bin.mem_writer(),
Self::Ihex => MemoryFileType::Ihex.mem_writer(), Self::Ihex => MemoryFileType::Ihex.mem_writer(),
Self::Elf => MemoryFileType::Elf.mem_writer(), Self::Elf => MemoryFileType::Elf.mem_writer(),
} }
} }
fn file_reader(
&self,
filename: &PathBuf,
) -> Result<Box<dyn ObjectFileReader>, std::io::Error> {
match self {
Self::Auto => MemoryFileType::read_object_file(filename),
_ => unimplemented!(),
}
}
} }
#[derive(Args, Debug)] #[derive(Args, Debug)]
@ -139,6 +149,8 @@ struct ReadArgs {
#[derive(Args, Debug)] #[derive(Args, Debug)]
struct WriteArgs { struct WriteArgs {
file: PathBuf, file: PathBuf,
#[arg(short = 't', long = "type", value_enum, default_value_t = ObjectFileType::Auto)]
filetype: ObjectFileType,
/// USB device to act on /// USB device to act on
#[command(flatten)] #[command(flatten)]
@ -212,12 +224,9 @@ fn write_file_to_flash(args: &WriteArgs) -> Result<(), Error> {
let api = HidApi::new()?; let api = HidApi::new()?;
let isp = connect_device(&api, &args.devspec)?; 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()))?; .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_start = args.addr.unwrap_or(isp.GetFlashStartAddress()?);
let flash_size = args.size.unwrap_or(isp.GetFlashSizeInBytes()?); 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) let write_pb: ProgressBar = ProgressBar::new(buf.len() as u64)
.with_style(read_write_style()) .with_style(read_write_style())