From 83ffc79ee1746a5b815fd9faa9415d52a4b31aca Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Mon, 3 Mar 2025 12:31:49 +0100 Subject: [PATCH 1/5] Use `ElfFile` instead of `dyn FirmwareImage` where able --- espflash/src/cli/mod.rs | 8 ++++---- espflash/src/flasher/mod.rs | 2 +- espflash/src/image_format.rs | 9 +++++---- espflash/src/targets/esp32.rs | 7 ++++--- espflash/src/targets/esp32c2.rs | 6 +++--- espflash/src/targets/esp32c3.rs | 7 ++++--- espflash/src/targets/esp32c6.rs | 7 ++++--- espflash/src/targets/esp32h2.rs | 7 ++++--- espflash/src/targets/esp32p4.rs | 7 ++++--- espflash/src/targets/esp32s2.rs | 7 ++++--- espflash/src/targets/esp32s3.rs | 7 ++++--- espflash/src/targets/mod.rs | 4 ++-- 12 files changed, 43 insertions(+), 35 deletions(-) diff --git a/espflash/src/cli/mod.rs b/espflash/src/cli/mod.rs index b9c7f4ac..9ac22f60 100644 --- a/espflash/src/cli/mod.rs +++ b/espflash/src/cli/mod.rs @@ -607,9 +607,9 @@ pub fn save_elf_as_image( if merge { // To get a chip revision, the connection is needed // For simplicity, the revision None is used - let image = - chip.into_target() - .get_flash_image(&elf, flash_data.clone(), None, xtal_freq)?; + let image = chip + .into_target() + .get_flash_image(elf, flash_data.clone(), None, xtal_freq)?; display_image_size(image.app_size(), image.part_size()); @@ -643,7 +643,7 @@ pub fn save_elf_as_image( } else { let image = chip .into_target() - .get_flash_image(&elf, flash_data, None, xtal_freq)?; + .get_flash_image(elf, flash_data, None, xtal_freq)?; display_image_size(image.app_size(), image.part_size()); diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index 30763b33..eddedd21 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -1057,7 +1057,7 @@ impl Flasher { let image = self.chip .into_target() - .get_flash_image(&elf, flash_data, chip_revision, xtal_freq)?; + .get_flash_image(elf, flash_data, chip_revision, xtal_freq)?; // When the `cli` feature is enabled, display the image size information. #[cfg(feature = "cli")] diff --git a/espflash/src/image_format.rs b/espflash/src/image_format.rs index 38e66ef1..1ee3f4ee 100644 --- a/espflash/src/image_format.rs +++ b/espflash/src/image_format.rs @@ -6,6 +6,7 @@ use bytemuck::{bytes_of, from_bytes, Pod, Zeroable}; use esp_idf_part::{AppType, DataType, Partition, PartitionTable, SubType, Type}; use log::debug; use sha2::{Digest, Sha256}; +use xmas_elf::ElfFile; use crate::{ elf::{FirmwareImage, Segment}, @@ -119,7 +120,7 @@ pub struct IdfBootloaderFormat<'a> { impl<'a> IdfBootloaderFormat<'a> { pub fn new( - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, chip: Chip, flash_data: FlashData, params: Esp32Params, @@ -190,7 +191,7 @@ impl<'a> IdfBootloaderFormat<'a> { // write the header of the app // use the same settings as the bootloader // just update the entry point - header.entry = image.entry(); + header.entry = elf.entry(); header.wp_pin = WP_PIN_DISABLED; header.chip_id = params.chip_id; @@ -199,8 +200,8 @@ impl<'a> IdfBootloaderFormat<'a> { let mut data = bytes_of(&header).to_vec(); - let flash_segments: Vec<_> = merge_adjacent_segments(image.rom_segments(chip).collect()); - let mut ram_segments: Vec<_> = merge_adjacent_segments(image.ram_segments(chip).collect()); + let flash_segments: Vec<_> = merge_adjacent_segments(elf.rom_segments(chip).collect()); + let mut ram_segments: Vec<_> = merge_adjacent_segments(elf.ram_segments(chip).collect()); let mut checksum = ESP_CHECKSUM_MAGIC; let mut segment_count = 0; diff --git a/espflash/src/targets/esp32.rs b/espflash/src/targets/esp32.rs index d7198b98..3516853a 100644 --- a/espflash/src/targets/esp32.rs +++ b/espflash/src/targets/esp32.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::{connection::Connection, targets::bytes_to_mac_addr}; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -154,7 +155,7 @@ impl Target for Esp32 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -183,7 +184,7 @@ impl Target for Esp32 { booloader, ); - IdfBootloaderFormat::new(image, Chip::Esp32, flash_data, params) + IdfBootloaderFormat::new(elf, Chip::Esp32, flash_data, params) } #[cfg(feature = "serialport")] diff --git a/espflash/src/targets/esp32c2.rs b/espflash/src/targets/esp32c2.rs index cc48eabd..28dd3a9c 100644 --- a/espflash/src/targets/esp32c2.rs +++ b/espflash/src/targets/esp32c2.rs @@ -1,11 +1,11 @@ use std::{collections::HashMap, ops::Range}; use log::debug; +use xmas_elf::ElfFile; #[cfg(feature = "serialport")] use crate::{connection::Connection, targets::bytes_to_mac_addr}; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -90,7 +90,7 @@ impl Target for Esp32c2 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -121,7 +121,7 @@ impl Target for Esp32c2 { booloader, ); - IdfBootloaderFormat::new(image, Chip::Esp32c2, flash_data, params) + IdfBootloaderFormat::new(elf, Chip::Esp32c2, flash_data, params) } #[cfg(feature = "serialport")] diff --git a/espflash/src/targets/esp32c3.rs b/espflash/src/targets/esp32c3.rs index 186384ad..409a115d 100644 --- a/espflash/src/targets/esp32c3.rs +++ b/espflash/src/targets/esp32c3.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::connection::Connection; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -78,7 +79,7 @@ impl Target for Esp32c3 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -90,7 +91,7 @@ impl Target for Esp32c3 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32c3, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32c3, flash_data, PARAMS) } fn spi_registers(&self) -> SpiRegisters { diff --git a/espflash/src/targets/esp32c6.rs b/espflash/src/targets/esp32c6.rs index 5237444d..fa23b080 100644 --- a/espflash/src/targets/esp32c6.rs +++ b/espflash/src/targets/esp32c6.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::connection::Connection; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -73,7 +74,7 @@ impl Target for Esp32c6 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -85,7 +86,7 @@ impl Target for Esp32c6 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32c6, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32c6, flash_data, PARAMS) } fn spi_registers(&self) -> SpiRegisters { diff --git a/espflash/src/targets/esp32h2.rs b/espflash/src/targets/esp32h2.rs index 9b72bd5f..4cbfffbc 100644 --- a/espflash/src/targets/esp32h2.rs +++ b/espflash/src/targets/esp32h2.rs @@ -1,9 +1,10 @@ use std::{collections::HashMap, ops::Range}; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::connection::Connection; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -80,7 +81,7 @@ impl Target for Esp32h2 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -92,7 +93,7 @@ impl Target for Esp32h2 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32h2, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32h2, flash_data, PARAMS) } fn spi_registers(&self) -> SpiRegisters { diff --git a/espflash/src/targets/esp32p4.rs b/espflash/src/targets/esp32p4.rs index 9a600c4d..8ba854ec 100644 --- a/espflash/src/targets/esp32p4.rs +++ b/espflash/src/targets/esp32p4.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::connection::Connection; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -69,7 +70,7 @@ impl Target for Esp32p4 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -81,7 +82,7 @@ impl Target for Esp32p4 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32p4, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32p4, flash_data, PARAMS) } fn spi_registers(&self) -> SpiRegisters { diff --git a/espflash/src/targets/esp32s2.rs b/espflash/src/targets/esp32s2.rs index 8b48ed53..cf655cbe 100644 --- a/espflash/src/targets/esp32s2.rs +++ b/espflash/src/targets/esp32s2.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::{connection::Connection, flasher::FLASH_WRITE_SIZE, targets::MAX_RAM_BLOCK_SIZE}; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -147,7 +148,7 @@ impl Target for Esp32s2 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -159,7 +160,7 @@ impl Target for Esp32s2 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32s2, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32s2, flash_data, PARAMS) } #[cfg(feature = "serialport")] diff --git a/espflash/src/targets/esp32s3.rs b/espflash/src/targets/esp32s3.rs index 8102a6f7..9b1e7f31 100644 --- a/espflash/src/targets/esp32s3.rs +++ b/espflash/src/targets/esp32s3.rs @@ -1,9 +1,10 @@ use std::ops::Range; +use xmas_elf::ElfFile; + #[cfg(feature = "serialport")] use crate::connection::Connection; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{Chip, Esp32Params, ReadEFuse, SpiRegisters, Target, XtalFrequency}, @@ -97,7 +98,7 @@ impl Target for Esp32s3 { fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, _chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, @@ -109,7 +110,7 @@ impl Target for Esp32s3 { }); } - IdfBootloaderFormat::new(image, Chip::Esp32s3, flash_data, PARAMS) + IdfBootloaderFormat::new(elf, Chip::Esp32s3, flash_data, PARAMS) } fn spi_registers(&self) -> SpiRegisters { diff --git a/espflash/src/targets/mod.rs b/espflash/src/targets/mod.rs index 5c23c946..35dbf741 100644 --- a/espflash/src/targets/mod.rs +++ b/espflash/src/targets/mod.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use strum::{Display, EnumIter, EnumString, VariantNames}; +use xmas_elf::ElfFile; #[cfg(feature = "serialport")] pub use self::flash_target::{Esp32Target, RamTarget}; @@ -18,7 +19,6 @@ use crate::{ targets::flash_target::{FlashTarget, MAX_RAM_BLOCK_SIZE}, }; use crate::{ - elf::FirmwareImage, flasher::{FlashData, FlashFrequency}, image_format::IdfBootloaderFormat, targets::{ @@ -306,7 +306,7 @@ pub trait Target: ReadEFuse { /// Build an image from the provided data for flashing fn get_flash_image<'a>( &self, - image: &'a dyn FirmwareImage<'a>, + elf: ElfFile<'a>, flash_data: FlashData, chip_revision: Option<(u32, u32)>, xtal_freq: XtalFrequency, From a8d986d2ea96ec5c22c37db848543915716879fb Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Fri, 28 Feb 2025 13:34:46 +0100 Subject: [PATCH 2/5] Convert the `image_format` module to a directory --- espflash/src/{image_format.rs => image_format/esp_idf.rs} | 0 espflash/src/image_format/mod.rs | 5 +++++ 2 files changed, 5 insertions(+) rename espflash/src/{image_format.rs => image_format/esp_idf.rs} (100%) create mode 100644 espflash/src/image_format/mod.rs diff --git a/espflash/src/image_format.rs b/espflash/src/image_format/esp_idf.rs similarity index 100% rename from espflash/src/image_format.rs rename to espflash/src/image_format/esp_idf.rs diff --git a/espflash/src/image_format/mod.rs b/espflash/src/image_format/mod.rs new file mode 100644 index 00000000..a443e630 --- /dev/null +++ b/espflash/src/image_format/mod.rs @@ -0,0 +1,5 @@ +//! Binary application image formats + +pub use self::esp_idf::IdfBootloaderFormat; + +mod esp_idf; From b99af18388ba2dfa6aea7868fe7aa573e1a2a826 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Mon, 3 Mar 2025 12:32:38 +0100 Subject: [PATCH 3/5] Eliminate the `elf` module, move its contents to `image_format` --- espflash/src/elf.rs | 194 --------------------- espflash/src/flasher/mod.rs | 2 +- espflash/src/image_format/esp_idf.rs | 2 +- espflash/src/image_format/mod.rs | 192 ++++++++++++++++++++ espflash/src/lib.rs | 3 +- espflash/src/targets/flash_target/esp32.rs | 2 +- espflash/src/targets/flash_target/mod.rs | 2 +- espflash/src/targets/flash_target/ram.rs | 2 +- 8 files changed, 198 insertions(+), 201 deletions(-) delete mode 100644 espflash/src/elf.rs diff --git a/espflash/src/elf.rs b/espflash/src/elf.rs deleted file mode 100644 index 26534f6d..00000000 --- a/espflash/src/elf.rs +++ /dev/null @@ -1,194 +0,0 @@ -//! ELF (Executable and Linkable Format) file operations - -use std::{ - borrow::Cow, - cmp::Ordering, - fmt::{Debug, Formatter}, - mem::take, - ops::AddAssign, -}; - -use xmas_elf::{ - sections::{SectionData, ShType}, - ElfFile, -}; - -use crate::targets::Chip; - -/// Operations for working with firmware images -pub trait FirmwareImage<'a> { - /// Firmware image entry point - fn entry(&self) -> u32; - - /// Firmware image segments - fn segments(&'a self) -> Box> + 'a>; - - /// Firmware image ROM segments - fn rom_segments(&'a self, chip: Chip) -> Box> + 'a> { - Box::new( - self.segments() - .filter(move |segment| chip.into_target().addr_is_flash(segment.addr)), - ) - } - - /// Firmware image RAM segments - fn ram_segments(&'a self, chip: Chip) -> Box> + 'a> { - Box::new( - self.segments() - .filter(move |segment| !chip.into_target().addr_is_flash(segment.addr)), - ) - } -} - -impl<'a> FirmwareImage<'a> for ElfFile<'a> { - fn entry(&self) -> u32 { - self.header.pt2.entry_point() as u32 - } - - fn segments(&'a self) -> Box> + 'a> { - Box::new( - self.section_iter() - .filter(|header| { - header.size() > 0 - && header.get_type() == Ok(ShType::ProgBits) - && header.offset() > 0 - && header.address() > 0 - }) - .flat_map(move |header| { - let addr = header.address() as u32; - let data = match header.get_data(self) { - Ok(SectionData::Undefined(data)) => data, - _ => return None, - }; - Some(Segment::new(addr, data)) - }), - ) - } -} - -/// A segment of code from the source ELF -#[derive(Default, Clone, Eq)] -pub struct Segment<'a> { - /// Base address of the code segment - pub addr: u32, - /// Segment data - pub data: Cow<'a, [u8]>, -} - -impl<'a> Segment<'a> { - pub fn new(addr: u32, data: &'a [u8]) -> Self { - let mut segment = Segment { - addr, - data: Cow::Borrowed(data), - }; - segment.pad_align(4); - - segment - } - - /// Split of the first `count` bytes into a new segment, adjusting the - /// remaining segment as needed - pub fn split_off(&mut self, count: usize) -> Self { - if count < self.data.len() { - let (head, tail) = match take(&mut self.data) { - Cow::Borrowed(data) => { - let (head, tail) = data.split_at(count); - (Cow::Borrowed(head), Cow::Borrowed(tail)) - } - Cow::Owned(mut data) => { - let tail = data.split_off(count); - (Cow::Owned(data), Cow::Owned(tail)) - } - }; - let new = Segment { - addr: self.addr, - data: head, - }; - self.addr += count as u32; - self.data = tail; - new - } else { - let new = self.clone(); - self.addr += self.size(); - self.data = Cow::Borrowed(&[]); - new - } - } - - /// Return the size of the segment - pub fn size(&self) -> u32 { - self.data.len() as u32 - } - - /// Return the data of the segment - pub fn data(&self) -> &[u8] { - self.data.as_ref() - } - - /// Pad the segment to the given alignment - pub fn pad_align(&mut self, align: usize) { - let padding = (align - self.data.len() % align) % align; - if padding > 0 { - let mut data = take(&mut self.data).into_owned(); - data.extend_from_slice(&[0; 4][0..padding]); - self.data = Cow::Owned(data); - } - } - - /// Borrow the segment for the given lifetime - pub fn borrow<'b>(&'b self) -> Segment<'b> - where - 'a: 'b, - { - Segment { - addr: self.addr, - data: Cow::Borrowed(self.data.as_ref()), - } - } -} - -impl AddAssign<&'_ [u8]> for Segment<'_> { - fn add_assign(&mut self, rhs: &'_ [u8]) { - let mut data = take(&mut self.data).into_owned(); - data.extend_from_slice(rhs); - self.data = Cow::Owned(data); - } -} - -#[allow(clippy::suspicious_op_assign_impl)] -impl AddAssign<&'_ Segment<'_>> for Segment<'_> { - fn add_assign(&mut self, rhs: &'_ Segment<'_>) { - let mut data = take(&mut self.data).into_owned(); - // Pad or truncate: - data.resize((rhs.addr - self.addr) as usize, 0); - data.extend_from_slice(rhs.data()); - self.data = Cow::Owned(data); - } -} - -impl Debug for Segment<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CodeSegment") - .field("addr", &self.addr) - .field("size", &self.size()) - .finish() - } -} - -impl PartialEq for Segment<'_> { - fn eq(&self, other: &Self) -> bool { - self.addr.eq(&other.addr) - } -} - -impl PartialOrd for Segment<'_> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Segment<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.addr.cmp(&other.addr) - } -} diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index eddedd21..33e976db 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -31,7 +31,6 @@ use crate::{ Connection, Port, }, - elf::{FirmwareImage, Segment}, error::{ConnectionError, ResultExt}, flasher::stubs::{ FlashStub, @@ -39,6 +38,7 @@ use crate::{ DEFAULT_TIMEOUT, EXPECTED_STUB_HANDSHAKE, }, + image_format::{FirmwareImage, Segment}, }; use crate::{ error::{ElfError, Error}, diff --git a/espflash/src/image_format/esp_idf.rs b/espflash/src/image_format/esp_idf.rs index 1ee3f4ee..c6b4d5c7 100644 --- a/espflash/src/image_format/esp_idf.rs +++ b/espflash/src/image_format/esp_idf.rs @@ -8,8 +8,8 @@ use log::debug; use sha2::{Digest, Sha256}; use xmas_elf::ElfFile; +use super::{FirmwareImage, Segment}; use crate::{ - elf::{FirmwareImage, Segment}, flasher::{FlashData, FlashFrequency, FlashMode, FlashSize}, targets::{Chip, Esp32Params}, Error, diff --git a/espflash/src/image_format/mod.rs b/espflash/src/image_format/mod.rs index a443e630..b52f1bba 100644 --- a/espflash/src/image_format/mod.rs +++ b/espflash/src/image_format/mod.rs @@ -1,5 +1,197 @@ //! Binary application image formats +use std::{ + borrow::Cow, + cmp::Ordering, + fmt::{Debug, Formatter}, + mem::take, + ops::AddAssign, +}; + +use xmas_elf::{ + sections::{SectionData, ShType}, + ElfFile, +}; + pub use self::esp_idf::IdfBootloaderFormat; +use crate::targets::Chip; mod esp_idf; + +/// Operations for working with firmware images +pub trait FirmwareImage<'a> { + /// Firmware image entry point + fn entry(&self) -> u32; + + /// Firmware image segments + fn segments(&'a self) -> Box> + 'a>; + + /// Firmware image ROM segments + fn rom_segments(&'a self, chip: Chip) -> Box> + 'a> { + Box::new( + self.segments() + .filter(move |segment| chip.into_target().addr_is_flash(segment.addr)), + ) + } + + /// Firmware image RAM segments + fn ram_segments(&'a self, chip: Chip) -> Box> + 'a> { + Box::new( + self.segments() + .filter(move |segment| !chip.into_target().addr_is_flash(segment.addr)), + ) + } +} + +impl<'a> FirmwareImage<'a> for ElfFile<'a> { + fn entry(&self) -> u32 { + self.header.pt2.entry_point() as u32 + } + + fn segments(&'a self) -> Box> + 'a> { + Box::new( + self.section_iter() + .filter(|header| { + header.size() > 0 + && header.get_type() == Ok(ShType::ProgBits) + && header.offset() > 0 + && header.address() > 0 + }) + .flat_map(move |header| { + let addr = header.address() as u32; + let data = match header.get_data(self) { + Ok(SectionData::Undefined(data)) => data, + _ => return None, + }; + Some(Segment::new(addr, data)) + }), + ) + } +} + +/// A segment of code from the source ELF +#[derive(Default, Clone, Eq)] +pub struct Segment<'a> { + /// Base address of the code segment + pub addr: u32, + /// Segment data + pub data: Cow<'a, [u8]>, +} + +impl<'a> Segment<'a> { + pub fn new(addr: u32, data: &'a [u8]) -> Self { + let mut segment = Segment { + addr, + data: Cow::Borrowed(data), + }; + segment.pad_align(4); + + segment + } + + /// Split of the first `count` bytes into a new segment, adjusting the + /// remaining segment as needed + pub fn split_off(&mut self, count: usize) -> Self { + if count < self.data.len() { + let (head, tail) = match take(&mut self.data) { + Cow::Borrowed(data) => { + let (head, tail) = data.split_at(count); + (Cow::Borrowed(head), Cow::Borrowed(tail)) + } + Cow::Owned(mut data) => { + let tail = data.split_off(count); + (Cow::Owned(data), Cow::Owned(tail)) + } + }; + let new = Segment { + addr: self.addr, + data: head, + }; + self.addr += count as u32; + self.data = tail; + new + } else { + let new = self.clone(); + self.addr += self.size(); + self.data = Cow::Borrowed(&[]); + new + } + } + + /// Return the size of the segment + pub fn size(&self) -> u32 { + self.data.len() as u32 + } + + /// Return the data of the segment + pub fn data(&self) -> &[u8] { + self.data.as_ref() + } + + /// Pad the segment to the given alignment + pub fn pad_align(&mut self, align: usize) { + let padding = (align - self.data.len() % align) % align; + if padding > 0 { + let mut data = take(&mut self.data).into_owned(); + data.extend_from_slice(&[0; 4][0..padding]); + self.data = Cow::Owned(data); + } + } + + /// Borrow the segment for the given lifetime + pub fn borrow<'b>(&'b self) -> Segment<'b> + where + 'a: 'b, + { + Segment { + addr: self.addr, + data: Cow::Borrowed(self.data.as_ref()), + } + } +} + +impl AddAssign<&'_ [u8]> for Segment<'_> { + fn add_assign(&mut self, rhs: &'_ [u8]) { + let mut data = take(&mut self.data).into_owned(); + data.extend_from_slice(rhs); + self.data = Cow::Owned(data); + } +} + +#[allow(clippy::suspicious_op_assign_impl)] +impl AddAssign<&'_ Segment<'_>> for Segment<'_> { + fn add_assign(&mut self, rhs: &'_ Segment<'_>) { + let mut data = take(&mut self.data).into_owned(); + // Pad or truncate: + data.resize((rhs.addr - self.addr) as usize, 0); + data.extend_from_slice(rhs.data()); + self.data = Cow::Owned(data); + } +} + +impl Debug for Segment<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CodeSegment") + .field("addr", &self.addr) + .field("size", &self.size()) + .finish() + } +} + +impl PartialEq for Segment<'_> { + fn eq(&self, other: &Self) -> bool { + self.addr.eq(&other.addr) + } +} + +impl PartialOrd for Segment<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Segment<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.addr.cmp(&other.addr) + } +} diff --git a/espflash/src/lib.rs b/espflash/src/lib.rs index 30d91a1c..24f499f8 100644 --- a/espflash/src/lib.rs +++ b/espflash/src/lib.rs @@ -30,8 +30,8 @@ #![deny(missing_debug_implementations, rust_2018_idioms)] pub use self::{ - elf::{FirmwareImage, Segment}, error::Error, + image_format::{FirmwareImage, Segment}, }; #[cfg(feature = "serialport")] @@ -41,7 +41,6 @@ pub mod flasher; pub mod image_format; pub mod targets; -mod elf; mod error; // Command-line interface diff --git a/espflash/src/targets/flash_target/esp32.rs b/espflash/src/targets/flash_target/esp32.rs index 69c0061d..3469f373 100644 --- a/espflash/src/targets/flash_target/esp32.rs +++ b/espflash/src/targets/flash_target/esp32.rs @@ -17,8 +17,8 @@ use crate::{ targets::FlashTarget, }; use crate::{ - elf::Segment, flasher::{SpiAttachParams, FLASH_SECTOR_SIZE}, + image_format::Segment, targets::Chip, Error, }; diff --git a/espflash/src/targets/flash_target/mod.rs b/espflash/src/targets/flash_target/mod.rs index c50de207..6d036c2d 100644 --- a/espflash/src/targets/flash_target/mod.rs +++ b/espflash/src/targets/flash_target/mod.rs @@ -1,6 +1,6 @@ pub(crate) use self::ram::MAX_RAM_BLOCK_SIZE; pub use self::{esp32::Esp32Target, ram::RamTarget}; -use crate::{connection::Connection, elf::Segment, Error}; +use crate::{connection::Connection, image_format::Segment, Error}; mod esp32; mod ram; diff --git a/espflash/src/targets/flash_target/ram.rs b/espflash/src/targets/flash_target/ram.rs index e5b2e514..ffee6441 100644 --- a/espflash/src/targets/flash_target/ram.rs +++ b/espflash/src/targets/flash_target/ram.rs @@ -7,7 +7,7 @@ use crate::{ flasher::ProgressCallbacks, targets::FlashTarget, }; -use crate::{elf::Segment, Error}; +use crate::{image_format::Segment, Error}; pub const MAX_RAM_BLOCK_SIZE: usize = 0x1800; From e1b0eeb7a44a54f2a393c53535a65cbf7fec6a2d Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Mon, 3 Mar 2025 10:36:37 +0100 Subject: [PATCH 4/5] Eliminate the `FirmwareImage` trait --- espflash/src/flasher/mod.rs | 8 +-- espflash/src/image_format/esp_idf.rs | 9 ++- espflash/src/image_format/mod.rs | 86 +++++++++++----------------- espflash/src/lib.rs | 5 +- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/espflash/src/flasher/mod.rs b/espflash/src/flasher/mod.rs index 33e976db..98efe3d6 100644 --- a/espflash/src/flasher/mod.rs +++ b/espflash/src/flasher/mod.rs @@ -38,7 +38,7 @@ use crate::{ DEFAULT_TIMEOUT, EXPECTED_STUB_HANDSHAKE, }, - image_format::{FirmwareImage, Segment}, + image_format::{ram_segments, rom_segments, Segment}, }; use crate::{ error::{ElfError, Error}, @@ -1012,19 +1012,19 @@ impl Flasher { mut progress: Option<&mut dyn ProgressCallbacks>, ) -> Result<(), Error> { let elf = ElfFile::new(elf_data).map_err(ElfError::from)?; - if elf.rom_segments(self.chip).next().is_some() { + if rom_segments(self.chip, &elf).next().is_some() { return Err(Error::ElfNotRamLoadable); } let mut target = self.chip.ram_target( - Some(elf.entry()), + Some(elf.header.pt2.entry_point() as u32), self.chip .into_target() .max_ram_block_size(&mut self.connection)?, ); target.begin(&mut self.connection).flashing()?; - for segment in elf.ram_segments(self.chip) { + for segment in ram_segments(self.chip, &elf) { target .write_segment(&mut self.connection, segment, &mut progress) .flashing()?; diff --git a/espflash/src/image_format/esp_idf.rs b/espflash/src/image_format/esp_idf.rs index c6b4d5c7..c6f0e36d 100644 --- a/espflash/src/image_format/esp_idf.rs +++ b/espflash/src/image_format/esp_idf.rs @@ -8,7 +8,7 @@ use log::debug; use sha2::{Digest, Sha256}; use xmas_elf::ElfFile; -use super::{FirmwareImage, Segment}; +use super::{ram_segments, rom_segments, Segment}; use crate::{ flasher::{FlashData, FlashFrequency, FlashMode, FlashSize}, targets::{Chip, Esp32Params}, @@ -191,8 +191,7 @@ impl<'a> IdfBootloaderFormat<'a> { // write the header of the app // use the same settings as the bootloader // just update the entry point - header.entry = elf.entry(); - + header.entry = elf.header.pt2.entry_point() as u32; header.wp_pin = WP_PIN_DISABLED; header.chip_id = params.chip_id; header.min_chip_rev_full = flash_data.min_chip_rev; @@ -200,8 +199,8 @@ impl<'a> IdfBootloaderFormat<'a> { let mut data = bytes_of(&header).to_vec(); - let flash_segments: Vec<_> = merge_adjacent_segments(elf.rom_segments(chip).collect()); - let mut ram_segments: Vec<_> = merge_adjacent_segments(elf.ram_segments(chip).collect()); + let flash_segments: Vec<_> = merge_adjacent_segments(rom_segments(chip, &elf).collect()); + let mut ram_segments: Vec<_> = merge_adjacent_segments(ram_segments(chip, &elf).collect()); let mut checksum = ESP_CHECKSUM_MAGIC; let mut segment_count = 0; diff --git a/espflash/src/image_format/mod.rs b/espflash/src/image_format/mod.rs index b52f1bba..1e1e5f02 100644 --- a/espflash/src/image_format/mod.rs +++ b/espflash/src/image_format/mod.rs @@ -18,57 +18,6 @@ use crate::targets::Chip; mod esp_idf; -/// Operations for working with firmware images -pub trait FirmwareImage<'a> { - /// Firmware image entry point - fn entry(&self) -> u32; - - /// Firmware image segments - fn segments(&'a self) -> Box> + 'a>; - - /// Firmware image ROM segments - fn rom_segments(&'a self, chip: Chip) -> Box> + 'a> { - Box::new( - self.segments() - .filter(move |segment| chip.into_target().addr_is_flash(segment.addr)), - ) - } - - /// Firmware image RAM segments - fn ram_segments(&'a self, chip: Chip) -> Box> + 'a> { - Box::new( - self.segments() - .filter(move |segment| !chip.into_target().addr_is_flash(segment.addr)), - ) - } -} - -impl<'a> FirmwareImage<'a> for ElfFile<'a> { - fn entry(&self) -> u32 { - self.header.pt2.entry_point() as u32 - } - - fn segments(&'a self) -> Box> + 'a> { - Box::new( - self.section_iter() - .filter(|header| { - header.size() > 0 - && header.get_type() == Ok(ShType::ProgBits) - && header.offset() > 0 - && header.address() > 0 - }) - .flat_map(move |header| { - let addr = header.address() as u32; - let data = match header.get_data(self) { - Ok(SectionData::Undefined(data)) => data, - _ => return None, - }; - Some(Segment::new(addr, data)) - }), - ) - } -} - /// A segment of code from the source ELF #[derive(Default, Clone, Eq)] pub struct Segment<'a> { @@ -195,3 +144,38 @@ impl Ord for Segment<'_> { self.addr.cmp(&other.addr) } } + +/// Returns an iterator over all RAM segments for a given chip and ELF file. +pub(crate) fn ram_segments<'a>( + chip: Chip, + elf: &'a ElfFile<'a>, +) -> Box> + 'a> { + Box::new(segments(elf).filter(move |segment| !chip.into_target().addr_is_flash(segment.addr))) +} + +/// Returns an iterator over all ROM segments for a given chip and ELF file. +pub(crate) fn rom_segments<'a>( + chip: Chip, + elf: &'a ElfFile<'a>, +) -> Box> + 'a> { + Box::new(segments(elf).filter(move |segment| chip.into_target().addr_is_flash(segment.addr))) +} + +fn segments<'a>(elf: &'a ElfFile<'a>) -> Box> + 'a> { + Box::new( + elf.section_iter() + .filter(|header| { + header.size() > 0 + && header.get_type() == Ok(ShType::ProgBits) + && header.offset() > 0 + && header.address() > 0 + }) + .flat_map(move |header| { + let addr = header.address() as u32; + match header.get_data(elf) { + Ok(SectionData::Undefined(data)) => Some(Segment::new(addr, data)), + _ => None, + } + }), + ) +} diff --git a/espflash/src/lib.rs b/espflash/src/lib.rs index 24f499f8..fa54af1e 100644 --- a/espflash/src/lib.rs +++ b/espflash/src/lib.rs @@ -29,10 +29,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(missing_debug_implementations, rust_2018_idioms)] -pub use self::{ - error::Error, - image_format::{FirmwareImage, Segment}, -}; +pub use self::{error::Error, image_format::Segment}; #[cfg(feature = "serialport")] #[cfg_attr(docsrs, doc(cfg(feature = "serialport")))] From 5caf81811f48f8464ce59f5c0a4370b3c0ff5425 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Wed, 5 Mar 2025 10:10:46 +0100 Subject: [PATCH 5/5] Update `CHANGELOG.md` --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f26ba59..910f6544 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,8 +38,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Removed - Removed the `libudev` feature (#742) -- The `FirmwareImage` trait no longer includes the `segments_with_load_addresses` function (#796) - Removed the `flasher::parse_partition_table` function (#798) +- The `FirmwareImage` trait has been removed (#802) +- The `elf` module has been removed, and its contents moved to the `image_format` module (#802) ## [3.3.0] - 2025-01-13