Skip to main content

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_INIT_ARRAY, 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    /// Return mutable access to the data of the segment
138    pub fn data_mut(&mut self) -> &mut [u8] {
139        self.data.to_mut()
140    }
141
142    /// Pad the segment to the given alignment
143    pub fn pad_align(&mut self, align: usize) {
144        let padding = (align - self.data.len() % align) % align;
145        if padding > 0 {
146            let mut data = take(&mut self.data).into_owned();
147            data.extend_from_slice(&[0; 4][0..padding]);
148            self.data = Cow::Owned(data);
149        }
150    }
151
152    /// Borrow the segment for the given lifetime
153    pub fn borrow<'b>(&'b self) -> Segment<'b>
154    where
155        'a: 'b,
156    {
157        Segment {
158            addr: self.addr,
159            data: Cow::Borrowed(self.data.as_ref()),
160        }
161    }
162}
163
164impl AddAssign<&'_ [u8]> for Segment<'_> {
165    fn add_assign(&mut self, rhs: &'_ [u8]) {
166        let mut data = take(&mut self.data).into_owned();
167        data.extend_from_slice(rhs);
168        self.data = Cow::Owned(data);
169    }
170}
171
172#[allow(clippy::suspicious_op_assign_impl)]
173impl AddAssign<&'_ Segment<'_>> for Segment<'_> {
174    fn add_assign(&mut self, rhs: &'_ Segment<'_>) {
175        let mut data = take(&mut self.data).into_owned();
176        // Pad or truncate:
177        data.resize((rhs.addr - self.addr) as usize, 0);
178        data.extend_from_slice(rhs.data());
179        self.data = Cow::Owned(data);
180    }
181}
182
183impl Debug for Segment<'_> {
184    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
185        f.debug_struct("CodeSegment")
186            .field("addr", &self.addr)
187            .field("size", &self.size())
188            .finish()
189    }
190}
191
192impl PartialEq for Segment<'_> {
193    fn eq(&self, other: &Self) -> bool {
194        self.addr.eq(&other.addr)
195    }
196}
197
198impl PartialOrd for Segment<'_> {
199    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
200        Some(self.cmp(other))
201    }
202}
203
204impl Ord for Segment<'_> {
205    fn cmp(&self, other: &Self) -> Ordering {
206        self.addr.cmp(&other.addr)
207    }
208}
209
210/// Returns an iterator over all RAM segments for a given chip and ELF file.
211pub(crate) fn ram_segments<'a>(
212    chip: Chip,
213    elf: &'a ElfFile<'a>,
214) -> impl Iterator<Item = Segment<'a>> {
215    segments(elf).filter(move |segment| !chip.addr_is_flash(segment.addr))
216}
217
218/// Returns an iterator over all ROM segments for a given chip and ELF file.
219pub(crate) fn rom_segments<'a>(
220    chip: Chip,
221    elf: &'a ElfFile<'a>,
222) -> impl Iterator<Item = Segment<'a>> {
223    segments(elf).filter(move |segment| chip.addr_is_flash(segment.addr))
224}
225
226fn segments<'a>(elf: &'a ElfFile<'a>) -> impl Iterator<Item = Segment<'a>> {
227    elf.sections()
228        .filter(|section| {
229            let header = section.elf_section_header();
230            let sh_type = header.sh_type(Endianness::Little);
231
232            section.size() > 0
233                && (sh_type == SHT_PROGBITS || sh_type == SHT_INIT_ARRAY)
234                && header.sh_offset.get(Endianness::Little) > 0
235                && section.address() > 0
236                && !is_empty(section.flags())
237        })
238        .flat_map(move |section| match section.data() {
239            Ok(data) => Some(Segment::new(section.address() as u32, data)),
240            _ => None,
241        })
242}
243
244fn is_empty(flags: object::SectionFlags) -> bool {
245    match flags {
246        object::SectionFlags::None => true,
247        object::SectionFlags::Elf { sh_flags } => sh_flags == 0,
248        _ => unreachable!(),
249    }
250}
251
252#[cfg(test)]
253mod test {
254    use object::read::elf::ElfFile;
255
256    use super::segments;
257
258    #[test]
259    fn test_overlapping_sections_are_removed() {
260        let elf_data: Vec<u8> = std::fs::read(
261            "tests/data/esp_hal_binary_with_overlapping_defmt_and_embedded_test_sections",
262        )
263        .unwrap();
264
265        let elf = ElfFile::parse(elf_data.as_slice()).unwrap();
266        let segments = segments(&elf).collect::<Vec<_>>();
267
268        let expected = [
269            // (address, size)
270            (0x3F400020, 256),   // .rodata_desc
271            (0x3F400120, 29152), // .rodata
272            (0x3FFB0000, 3716),  // .data
273            (0x40080000, 1024),  // .vectors
274            (0x40080400, 5088),  // .rwtext
275            (0x400D0020, 62654), // .text
276        ];
277
278        assert_eq!(segments.len(), expected.len());
279
280        for seg in segments {
281            let addr_and_len = (seg.addr, seg.size());
282            assert!(
283                expected.contains(&addr_and_len),
284                "Unexpected section: {addr_and_len:x?}"
285            )
286        }
287    }
288}