espflash/
elf.rs

1//! ELF (Executable and Linkable Format) file operations
2
3use std::{
4    borrow::Cow,
5    cmp::Ordering,
6    fmt::{Debug, Formatter},
7    mem::take,
8    ops::AddAssign,
9};
10
11use xmas_elf::{
12    program::Type,
13    sections::{SectionData, ShType},
14    ElfFile,
15};
16
17use crate::{
18    error::{ElfError, Error},
19    targets::Chip,
20};
21
22/// Operations for working with firmware images
23pub trait FirmwareImage<'a> {
24    /// Firmware image entry point
25    fn entry(&self) -> u32;
26
27    /// Firmware image segments
28    fn segments(&'a self) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a>;
29
30    /// Firmware image segments, with their associated load addresses
31    fn segments_with_load_addresses(&'a self) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a>;
32
33    /// Firmware image ROM segments
34    fn rom_segments(&'a self, chip: Chip) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a> {
35        Box::new(
36            self.segments()
37                .filter(move |segment| chip.into_target().addr_is_flash(segment.addr)),
38        )
39    }
40
41    /// Firmware image RAM segments
42    fn ram_segments(&'a self, chip: Chip) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a> {
43        Box::new(
44            self.segments()
45                .filter(move |segment| !chip.into_target().addr_is_flash(segment.addr)),
46        )
47    }
48}
49
50/// A firmware image built from an ELF file
51pub struct ElfFirmwareImage<'a> {
52    elf: ElfFile<'a>,
53}
54
55impl<'a> ElfFirmwareImage<'a> {
56    pub fn new(elf: ElfFile<'a>) -> Self {
57        Self { elf }
58    }
59}
60
61impl<'a> TryFrom<&'a [u8]> for ElfFirmwareImage<'a> {
62    type Error = Error;
63
64    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
65        let elf = ElfFile::new(value).map_err(ElfError::from)?;
66
67        let image = ElfFirmwareImage::new(elf);
68
69        Ok(image)
70    }
71}
72
73impl<'a> FirmwareImage<'a> for ElfFirmwareImage<'a> {
74    fn entry(&self) -> u32 {
75        self.elf.header.pt2.entry_point() as u32
76    }
77
78    fn segments(&'a self) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a> {
79        Box::new(
80            self.elf
81                .section_iter()
82                .filter(|header| {
83                    header.size() > 0
84                        && header.get_type() == Ok(ShType::ProgBits)
85                        && header.offset() > 0
86                        && header.address() > 0
87                })
88                .flat_map(move |header| {
89                    let addr = header.address() as u32;
90                    let data = match header.get_data(&self.elf) {
91                        Ok(SectionData::Undefined(data)) => data,
92                        _ => return None,
93                    };
94                    Some(CodeSegment::new(addr, data))
95                }),
96        )
97    }
98
99    fn segments_with_load_addresses(&'a self) -> Box<dyn Iterator<Item = CodeSegment<'a>> + 'a> {
100        Box::new(
101            self.elf
102                .program_iter()
103                .filter(|header| {
104                    header.file_size() > 0
105                        && header.get_type() == Ok(Type::Load)
106                        && header.offset() > 0
107                })
108                .flat_map(move |header| {
109                    let addr = header.physical_addr() as u32;
110                    let from = header.offset() as usize;
111                    let to = header.offset() as usize + header.file_size() as usize;
112                    let data = &self.elf.input[from..to];
113                    Some(CodeSegment::new(addr, data))
114                }),
115        )
116    }
117}
118
119#[derive(Eq, Clone, Default)]
120/// A segment of code from the source ELF
121pub struct CodeSegment<'a> {
122    /// Base address of the code segment
123    pub addr: u32,
124    data: Cow<'a, [u8]>,
125}
126
127impl<'a> CodeSegment<'a> {
128    pub fn new(addr: u32, data: &'a [u8]) -> Self {
129        let mut segment = CodeSegment {
130            addr,
131            data: Cow::Borrowed(data),
132        };
133        segment.pad_align(4);
134        segment
135    }
136
137    /// Split of the first `count` bytes into a new segment, adjusting the
138    /// remaining segment as needed
139    pub fn split_off(&mut self, count: usize) -> Self {
140        if count < self.data.len() {
141            let (head, tail) = match take(&mut self.data) {
142                Cow::Borrowed(data) => {
143                    let (head, tail) = data.split_at(count);
144                    (Cow::Borrowed(head), Cow::Borrowed(tail))
145                }
146                Cow::Owned(mut data) => {
147                    let tail = data.split_off(count);
148                    (Cow::Owned(data), Cow::Owned(tail))
149                }
150            };
151            let new = CodeSegment {
152                addr: self.addr,
153                data: head,
154            };
155            self.addr += count as u32;
156            self.data = tail;
157            new
158        } else {
159            let new = self.clone();
160            self.addr += self.size();
161            self.data = Cow::Borrowed(&[]);
162            new
163        }
164    }
165
166    /// Return the size of the segment
167    pub fn size(&self) -> u32 {
168        self.data.len() as u32
169    }
170
171    /// Return the data of the segment
172    pub fn data(&self) -> &[u8] {
173        self.data.as_ref()
174    }
175
176    /// Pad the segment to the given alignment
177    pub fn pad_align(&mut self, align: usize) {
178        let padding = (align - self.data.len() % align) % align;
179        if padding > 0 {
180            let mut data = take(&mut self.data).into_owned();
181            data.extend_from_slice(&[0; 4][0..padding]);
182            self.data = Cow::Owned(data);
183        }
184    }
185}
186
187impl AddAssign<&'_ [u8]> for CodeSegment<'_> {
188    fn add_assign(&mut self, rhs: &'_ [u8]) {
189        let mut data = take(&mut self.data).into_owned();
190        data.extend_from_slice(rhs);
191        self.data = Cow::Owned(data);
192    }
193}
194
195impl AddAssign<&'_ CodeSegment<'_>> for CodeSegment<'_> {
196    fn add_assign(&mut self, rhs: &'_ CodeSegment<'_>) {
197        let mut data = take(&mut self.data).into_owned();
198        // pad or truncate
199        #[allow(clippy::suspicious_op_assign_impl)]
200        data.resize((rhs.addr - self.addr) as usize, 0);
201        data.extend_from_slice(rhs.data());
202        self.data = Cow::Owned(data);
203    }
204}
205
206impl Debug for CodeSegment<'_> {
207    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
208        f.debug_struct("CodeSegment")
209            .field("addr", &self.addr)
210            .field("size", &self.size())
211            .finish()
212    }
213}
214
215impl PartialEq for CodeSegment<'_> {
216    fn eq(&self, other: &Self) -> bool {
217        self.addr.eq(&other.addr)
218    }
219}
220
221impl PartialOrd for CodeSegment<'_> {
222    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
223        Some(self.cmp(other))
224    }
225}
226
227impl Ord for CodeSegment<'_> {
228    fn cmp(&self, other: &Self) -> Ordering {
229        self.addr.cmp(&other.addr)
230    }
231}
232
233#[derive(Clone)]
234/// A segment of data to write to the flash
235pub struct RomSegment<'a> {
236    /// ROM address at which the segment begins
237    pub addr: u32,
238    /// Segment data
239    pub data: Cow<'a, [u8]>,
240}
241
242impl<'a> RomSegment<'a> {
243    pub fn borrow<'b>(&'b self) -> RomSegment<'b>
244    where
245        'a: 'b,
246    {
247        RomSegment {
248            addr: self.addr,
249            data: Cow::Borrowed(self.data.as_ref()),
250        }
251    }
252}
253
254impl<'a> From<CodeSegment<'a>> for RomSegment<'a> {
255    fn from(segment: CodeSegment<'a>) -> Self {
256        RomSegment {
257            addr: segment.addr,
258            data: segment.data,
259        }
260    }
261}