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:
parent
ee007da5d4
commit
51ad57bdaf
127
src/file.rs
127
src/file.rs
@ -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)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
23
src/main.rs
23
src/main.rs
@ -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())
|
||||||
|
Loading…
Reference in New Issue
Block a user