1use 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#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
29#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[non_exhaustive]
31pub enum ImageFormatKind {
32 #[default]
36 EspIdf,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
41#[non_exhaustive]
42pub enum ImageFormat<'a> {
43 EspIdf(IdfBootloaderFormat<'a>),
47}
48
49impl<'a> ImageFormat<'a> {
50 pub fn flash_segments(self) -> Vec<Segment<'a>> {
52 match self {
53 ImageFormat::EspIdf(idf) => idf.flash_segments().collect(),
54 }
55 }
56
57 pub fn ota_segments(self) -> Vec<Segment<'a>> {
59 match self {
60 ImageFormat::EspIdf(idf) => idf.ota_segments().collect(),
61 }
62 }
63
64 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#[derive(Default, Clone, Eq, Deserialize, Serialize)]
80pub struct Segment<'a> {
81 pub addr: u32,
83 pub data: Cow<'a, [u8]>,
85}
86
87impl<'a> Segment<'a> {
88 pub fn new(addr: u32, data: &'a [u8]) -> Self {
90 Segment {
93 addr,
94 data: Cow::Borrowed(data),
95 }
96 }
97
98 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 pub fn size(&self) -> u32 {
129 self.data.len() as u32
130 }
131
132 pub fn data(&self) -> &[u8] {
134 self.data.as_ref()
135 }
136
137 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 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 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
205pub(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
213pub(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 (0x3F400020, 256), (0x3F400120, 29152), (0x3FFB0000, 3716), (0x40080000, 1024), (0x40080400, 5088), (0x400D0020, 62654), ];
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}