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_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#[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 data_mut(&mut self) -> &mut [u8] {
139 self.data.to_mut()
140 }
141
142 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 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 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
210pub(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
218pub(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 (0x3F400020, 256), (0x3F400120, 29152), (0x3FFB0000, 3716), (0x40080000, 1024), (0x40080400, 5088), (0x400D0020, 62654), ];
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}