1use crate::{
2 address_range::{self, AddressRange, RP2040_ADDRESS_RANGES_FLASH, RP2040_ADDRESS_RANGES_RAM},
3};
4use assert_into::AssertInto;
5use log::debug;
6use std::{
7 cmp::min,
8 collections::BTreeMap,
9 error::Error,
10 io::{Read, Seek, SeekFrom},
11 mem,
12};
13use zerocopy::{FromBytes, IntoBytes};
14
15const ELF_MAGIC: u32 = 0x464c457f;
16const PT_LOAD: u32 = 0x00000001;
17
18pub const LOG2_PAGE_SIZE: u32 = 8;
19pub const PAGE_SIZE: u32 = 1 << LOG2_PAGE_SIZE;
20
21#[allow(unused)]
22#[repr(packed)]
23#[derive(IntoBytes, Copy, Clone, Default, Debug, FromBytes)]
24pub struct ElfHeader {
25 pub magic: u32,
26 pub arch_class: u8,
27 pub endianness: u8,
28 pub version: u8,
29 pub abi: u8,
30 pub abi_version: u8,
31 pub pad: [u8; 7],
32 pub typ: u16,
33 pub machine: u16,
34 pub version2: u32,
35}
36
37#[allow(unused)]
38#[repr(packed)]
39#[derive(IntoBytes, Copy, Clone, Default, Debug, FromBytes)]
40pub struct Elf32Header {
41 pub common: ElfHeader,
42 pub entry: u32,
43 pub ph_offset: u32,
44 pub sh_offset: u32,
45 pub flags: u32,
46 pub eh_size: u16,
47 pub ph_entry_size: u16,
48 pub ph_num: u16,
49 pub sh_entry_size: u16,
50 pub sh_num: u16,
51 pub sh_str_index: u16,
52}
53
54impl Elf32Header {
55 pub(crate) fn from_read(input: &mut impl Read) -> Result<Self, Box<dyn Error>> {
57 let mut eh = Elf32Header::default();
58
59 input.read_exact(eh.as_mut_bytes())?;
60
61 if eh.common.magic != ELF_MAGIC {
62 return Err("Not an ELF file".into());
63 }
64 if eh.common.version != 1 || eh.common.version2 != 1 {
65 return Err("Unrecognized ELF version".into());
66 }
67 if eh.common.arch_class != 1 || eh.common.endianness != 1 {
68 return Err("Require 32 bit little-endian ELF".into());
69 }
70 if eh.eh_size != mem::size_of::<Elf32Header>().assert_into() {
71 return Err("Invalid ELF32 format".into());
72 }
73 if eh.common.abi != 0 && eh.common.abi != 3 {
74 return Err("Unrecognized ABI".into());
75 }
76
77 Ok(eh)
78 }
79
80 pub(crate) fn read_elf32_ph_entries(
81 &self,
82 input: &mut impl Read,
83 ) -> Result<Vec<Elf32PhEntry>, Box<dyn Error>> {
84 if self.ph_entry_size != mem::size_of::<Elf32PhEntry>().assert_into() {
85 return Err("Invalid ELF32 program header".into());
86 }
87
88 let mut entries: Vec<Elf32PhEntry> = (0..self.ph_num).map(|_| Default::default()).collect();
89 input.read_exact(entries.as_mut_slice().as_mut_bytes())?;
90
91 Ok(entries)
92 }
93
94 pub(crate) fn is_ram_binary(&self, entries: &[Elf32PhEntry]) -> Option<bool> {
96 for entry in entries {
97 if entry.typ == PT_LOAD && entry.memsz > 0 {
98 let mapped_size = entry.filez.min(entry.memsz);
99 if mapped_size > 0 {
100 if self.entry >= entry.vaddr && self.entry < entry.vaddr + mapped_size {
103 let effective_entry = self.entry + entry.paddr - entry.vaddr;
104 if RP2040_ADDRESS_RANGES_RAM.is_address_initialized(effective_entry) {
105 return Some(true);
106 } else if RP2040_ADDRESS_RANGES_FLASH
107 .is_address_initialized(effective_entry)
108 {
109 return Some(false);
110 }
111 }
112 }
113 }
114 }
115
116 None
117 }
118}
119
120#[allow(unused)]
121#[repr(packed)]
122#[derive(IntoBytes, Copy, Clone, Default, Debug, FromBytes)]
123pub struct Elf32PhEntry {
124 pub typ: u32,
125 pub offset: u32,
126 pub vaddr: u32,
127 pub paddr: u32,
128 pub filez: u32,
129 pub memsz: u32,
130 pub flags: u32,
131 pub align: u32,
132}
133
134#[derive(Copy, Clone, Debug, Default)]
135pub struct PageFragment {
136 pub file_offset: u32,
137 pub page_offset: u32,
138 pub bytes: u32,
139}
140
141pub fn realize_page(
142 input: &mut (impl Read + Seek),
143 fragments: &[PageFragment],
144 buf: &mut [u8],
145) -> Result<(), Box<dyn Error>> {
146 assert!(buf.len() >= PAGE_SIZE.assert_into());
147
148 for frag in fragments {
149 assert!(frag.page_offset < PAGE_SIZE && frag.page_offset + frag.bytes <= PAGE_SIZE);
150
151 input.seek(SeekFrom::Start(frag.file_offset.assert_into()))?;
152
153 input.read_exact(
154 &mut buf[frag.page_offset.assert_into()..(frag.page_offset + frag.bytes).assert_into()],
155 )?;
156 }
157
158 Ok(())
159}
160
161pub trait AddressRangesExt<'a>: IntoIterator<Item = &'a AddressRange> + Clone {
162 fn range_for(&self, addr: u32) -> Option<&'a AddressRange> {
163 self.clone()
164 .into_iter()
165 .find(|r| r.from <= addr && r.to > addr)
166 }
167
168 fn is_address_initialized(&self, addr: u32) -> bool {
169 let range = if let Some(range) = self.range_for(addr) {
170 range
171 } else {
172 return false;
173 };
174
175 matches!(range.typ, address_range::AddressRangeType::Contents)
176 }
177
178 fn check_address_range(
180 &self,
181 addr: u32,
182 vaddr: u32,
183 size: u32,
184 uninitialized: bool,
185 ) -> Result<AddressRange, Box<dyn Error>> {
186 for range in self.clone().into_iter() {
187 if range.from <= addr && range.to >= addr + size {
188 if range.typ == address_range::AddressRangeType::NoContents && !uninitialized {
189 return Err(format!(
190 "ELF contains memory contents for uninitialized memory at {addr:08x}"
191 )
192 .into());
193 }
194
195 debug!(
196 "{} segment {:#08x}->{:#08x} ({:#08x}->{:#08x})",
197 if uninitialized {
198 "Uninitialized"
199 } else {
200 "Mapped"
201 },
202 addr,
203 addr + size,
204 vaddr,
205 vaddr + size
206 );
207 return Ok(*range);
208 }
209 }
210 Err(format!(
211 "Memory segment {:#08x}->{:#08x} is outside of valid address range for device",
212 addr,
213 addr + size
214 )
215 .into())
216 }
217
218 fn check_elf32_ph_entries(
219 &self,
220 entries: &[Elf32PhEntry],
221 ) -> Result<BTreeMap<u32, Vec<PageFragment>>, Box<dyn Error>> {
222 let mut pages = BTreeMap::<u32, Vec<PageFragment>>::new();
223
224 for entry in entries {
225 if entry.typ == PT_LOAD && entry.memsz > 0 {
226 let mapped_size = min(entry.filez, entry.memsz);
227
228 if mapped_size > 0 {
229 let ar =
230 self.check_address_range(entry.paddr, entry.vaddr, mapped_size, false)?;
231
232 if ar.typ != address_range::AddressRangeType::Contents {
234 debug!("ignored");
235 continue;
236 }
237 let mut addr = entry.paddr;
238 let mut remaining = mapped_size;
239 let mut file_offset = entry.offset;
240 while remaining > 0 {
241 let off = addr & (PAGE_SIZE - 1);
242 let len = min(remaining, PAGE_SIZE - off);
243
244 let fragments = pages.entry(addr - off).or_default();
246
247 for fragment in fragments.iter() {
251 if (off < fragment.page_offset + fragment.bytes)
252 != ((off + len) <= fragment.page_offset)
253 {
254 return Err("In memory segments overlap".into());
255 }
256 }
257 fragments.push(PageFragment {
258 file_offset,
259 page_offset: off,
260 bytes: len,
261 });
262 addr += len;
263 file_offset += len;
264 remaining -= len;
265 }
266 if entry.memsz > entry.filez {
267 self.check_address_range(
269 entry.paddr + entry.filez,
270 entry.vaddr + entry.filez,
271 entry.memsz - entry.filez,
272 true,
273 )?;
274 }
275 }
276 }
277 }
278
279 Ok(pages)
280 }
281}
282
283impl<'a, T> AddressRangesExt<'a> for T where T: IntoIterator<Item = &'a AddressRange> + Clone {}