espflash/image_format/
mod.rs

1//! Binary application image formats
2
3use std::{
4    borrow::Cow,
5    cmp::Ordering,
6    collections::HashMap,
7    fmt::{Debug, Formatter},
8    mem::take,
9    ops::AddAssign,
10};
11
12use object::{
13    Endianness,
14    Object as _,
15    ObjectSection as _,
16    elf::SHT_PROGBITS,
17    read::elf::{ElfFile32 as ElfFile, SectionHeader},
18};
19use serde::{Deserialize, Serialize};
20
21pub use self::metadata::Metadata;
22use crate::{image_format::idf::IdfBootloaderFormat, target::Chip};
23
24pub mod idf;
25mod metadata;
26
27/// Supported binary application image formats
28#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
29#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[non_exhaustive]
31pub enum ImageFormatKind {
32    /// ESP-IDF application image format
33    ///
34    /// See: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html>
35    #[default]
36    EspIdf,
37}
38
39/// Binary application image format data
40#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
41#[non_exhaustive]
42pub enum ImageFormat<'a> {
43    /// ESP-IDF application image format
44    ///
45    /// See: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html>
46    EspIdf(IdfBootloaderFormat<'a>),
47}
48
49impl<'a> ImageFormat<'a> {
50    /// Returns all flashable data segments
51    pub fn flash_segments(self) -> Vec<Segment<'a>> {
52        match self {
53            ImageFormat::EspIdf(idf) => idf.flash_segments().collect(),
54        }
55    }
56
57    /// Returns all data segments required for OTA updates
58    pub fn ota_segments(self) -> Vec<Segment<'a>> {
59        match self {
60            ImageFormat::EspIdf(idf) => idf.ota_segments().collect(),
61        }
62    }
63
64    /// Returns metadata about the application image
65    pub fn metadata(&self) -> HashMap<&str, String> {
66        match self {
67            ImageFormat::EspIdf(idf) => idf.metadata(),
68        }
69    }
70}
71
72impl<'a> From<IdfBootloaderFormat<'a>> for ImageFormat<'a> {
73    fn from(idf: IdfBootloaderFormat<'a>) -> Self {
74        Self::EspIdf(idf)
75    }
76}
77
78/// A segment of code from the source ELF
79#[derive(Default, Clone, Eq, Deserialize, Serialize)]
80pub struct Segment<'a> {
81    /// Base address of the code segment
82    pub addr: u32,
83    /// Segment data
84    pub data: Cow<'a, [u8]>,
85}
86
87impl<'a> Segment<'a> {
88    /// Creates a new [`Segment`].
89    pub fn new(addr: u32, data: &'a [u8]) -> Self {
90        // Do not pad the data here, as it might result in overlapping segments
91        // in the ELF file. The padding should be done after merging adjacent segments.
92        Segment {
93            addr,
94            data: Cow::Borrowed(data),
95        }
96    }
97
98    /// Splits off the first `count` bytes into a new segment, adjusting the
99    /// remaining segment as needed.
100    pub fn split_off(&mut self, count: usize) -> Self {
101        if count < self.data.len() {
102            let (head, tail) = match take(&mut self.data) {
103                Cow::Borrowed(data) => {
104                    let (head, tail) = data.split_at(count);
105                    (Cow::Borrowed(head), Cow::Borrowed(tail))
106                }
107                Cow::Owned(mut data) => {
108                    let tail = data.split_off(count);
109                    (Cow::Owned(data), Cow::Owned(tail))
110                }
111            };
112            let new = Segment {
113                addr: self.addr,
114                data: head,
115            };
116            self.addr += count as u32;
117            self.data = tail;
118            new
119        } else {
120            let new = self.clone();
121            self.addr += self.size();
122            self.data = Cow::Borrowed(&[]);
123            new
124        }
125    }
126
127    /// Return the size of the segment
128    pub fn size(&self) -> u32 {
129        self.data.len() as u32
130    }
131
132    /// Return the data of the segment
133    pub fn data(&self) -> &[u8] {
134        self.data.as_ref()
135    }
136
137    /// Pad the segment to the given alignment
138    pub fn pad_align(&mut self, align: usize) {
139        let padding = (align - self.data.len() % align) % align;
140        if padding > 0 {
141            let mut data = take(&mut self.data).into_owned();
142            data.extend_from_slice(&[0; 4][0..padding]);
143            self.data = Cow::Owned(data);
144        }
145    }
146
147    /// Borrow the segment for the given lifetime
148    pub fn borrow<'b>(&'b self) -> Segment<'b>
149    where
150        'a: 'b,
151    {
152        Segment {
153            addr: self.addr,
154            data: Cow::Borrowed(self.data.as_ref()),
155        }
156    }
157}
158
159impl AddAssign<&'_ [u8]> for Segment<'_> {
160    fn add_assign(&mut self, rhs: &'_ [u8]) {
161        let mut data = take(&mut self.data).into_owned();
162        data.extend_from_slice(rhs);
163        self.data = Cow::Owned(data);
164    }
165}
166
167#[allow(clippy::suspicious_op_assign_impl)]
168impl AddAssign<&'_ Segment<'_>> for Segment<'_> {
169    fn add_assign(&mut self, rhs: &'_ Segment<'_>) {
170        let mut data = take(&mut self.data).into_owned();
171        // Pad or truncate:
172        data.resize((rhs.addr - self.addr) as usize, 0);
173        data.extend_from_slice(rhs.data());
174        self.data = Cow::Owned(data);
175    }
176}
177
178impl Debug for Segment<'_> {
179    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180        f.debug_struct("CodeSegment")
181            .field("addr", &self.addr)
182            .field("size", &self.size())
183            .finish()
184    }
185}
186
187impl PartialEq for Segment<'_> {
188    fn eq(&self, other: &Self) -> bool {
189        self.addr.eq(&other.addr)
190    }
191}
192
193impl PartialOrd for Segment<'_> {
194    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
195        Some(self.cmp(other))
196    }
197}
198
199impl Ord for Segment<'_> {
200    fn cmp(&self, other: &Self) -> Ordering {
201        self.addr.cmp(&other.addr)
202    }
203}
204
205/// Returns an iterator over all RAM segments for a given chip and ELF file.
206pub(crate) fn ram_segments<'a>(
207    chip: Chip,
208    elf: &'a ElfFile<'a>,
209) -> impl Iterator<Item = Segment<'a>> {
210    segments(elf).filter(move |segment| !chip.addr_is_flash(segment.addr))
211}
212
213/// Returns an iterator over all ROM segments for a given chip and ELF file.
214pub(crate) fn rom_segments<'a>(
215    chip: Chip,
216    elf: &'a ElfFile<'a>,
217) -> impl Iterator<Item = Segment<'a>> {
218    segments(elf).filter(move |segment| chip.addr_is_flash(segment.addr))
219}
220
221fn segments<'a>(elf: &'a ElfFile<'a>) -> impl Iterator<Item = Segment<'a>> {
222    elf.sections()
223        .filter(|section| {
224            let header = section.elf_section_header();
225
226            section.size() > 0
227                && header.sh_type(Endianness::Little) == SHT_PROGBITS
228                && header.sh_offset.get(Endianness::Little) > 0
229                && section.address() > 0
230                && !is_empty(section.flags())
231        })
232        .flat_map(move |section| match section.data() {
233            Ok(data) => Some(Segment::new(section.address() as u32, data)),
234            _ => None,
235        })
236}
237
238fn is_empty(flags: object::SectionFlags) -> bool {
239    match flags {
240        object::SectionFlags::None => true,
241        object::SectionFlags::Elf { sh_flags } => sh_flags == 0,
242        _ => unreachable!(),
243    }
244}
245
246#[cfg(test)]
247mod test {
248    use object::read::elf::ElfFile;
249
250    use super::segments;
251
252    #[test]
253    fn test_overlapping_sections_are_removed() {
254        let elf_data: Vec<u8> = std::fs::read(
255            "tests/data/esp_hal_binary_with_overlapping_defmt_and_embedded_test_sections",
256        )
257        .unwrap();
258
259        let elf = ElfFile::parse(elf_data.as_slice()).unwrap();
260        let segments = segments(&elf).collect::<Vec<_>>();
261
262        let expected = [
263            // (address, size)
264            (0x3F400020, 256),   // .rodata_desc
265            (0x3F400120, 29152), // .rodata
266            (0x3FFB0000, 3716),  // .data
267            (0x40080000, 1024),  // .vectors
268            (0x40080400, 5088),  // .rwtext
269            (0x400D0020, 62654), // .text
270        ];
271
272        assert_eq!(segments.len(), expected.len());
273
274        for seg in segments {
275            let addr_and_len = (seg.addr, seg.size());
276            assert!(
277                expected.contains(&addr_and_len),
278                "Unexpected section: {addr_and_len:x?}"
279            )
280        }
281    }
282}