1use std::convert::TryInto;
2use std::fmt;
3use std::fs::File;
4use std::io::{Cursor, Read, Seek, SeekFrom, Write};
5use std::path::Path;
6
7use bitflags::bitflags;
8use log::debug;
9use xmas_elf::ElfFile;
10use xmas_elf::program::Type as ProgramType;
11use xmas_elf::sections::ShType;
12use xmas_elf::sections::{SHF_ALLOC, SHF_EXECINSTR, SHF_WRITE};
14
15bitflags! {
16 pub struct MiniElfFlags: u8 {
17 const NONE = 0;
18 const WRITE = 1;
19 const NOCOPY = 2;
20 const EXECUTE = 4;
21 const EH_FRAME = 8;
22 const EH_HEADER = 0x10;
23 }
24}
25
26pub struct ProgramDescription {
27 pub text_offset: u32,
29
30 pub text_size: u32,
32
33 pub data_offset: u32,
35
36 pub data_size: u32,
38
39 pub poke_table: Vec<(u32, u32)>,
42
43 pub clear_size: u32,
45
46 pub bss_size: u32,
48
49 pub entry_point: u32,
51
52 pub program: Vec<u8>,
54}
55
56#[derive(Debug)]
57pub struct MiniElfSection {
58 pub virt: u32,
59 pub size: u32,
60 pub flags: MiniElfFlags,
61 pub name: String,
62}
63
64impl fmt::Display for MiniElfSection {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(
67 f,
68 "Section {:13} {:6} bytes loading into {:08x}..{:08x} flags: {:?}",
69 self.name,
70 self.size,
71 self.virt,
72 self.virt + self.size,
73 self.flags
74 )
75 }
76}
77
78pub struct MiniElf {
80 pub entry_point: u32,
82
83 pub sections: Vec<MiniElfSection>,
85
86 pub program: Vec<u8>,
88
89 pub alignment_offset: usize,
91}
92
93#[derive(Debug)]
94pub enum ElfReadError {
95 WrongReadSize(u64 , u64 ),
97
98 SeekFromEndError(std::io::Error),
100
101 ReadFileError(std::io::Error),
103
104 OpenElfError(std::io::Error),
106
107 ParseElfError(&'static str),
109
110 SectionRangeError,
112
113 SectionNotAligned(String , usize ),
115
116 FileSeekError(std::io::Error),
118
119 WriteSectionError(std::io::Error),
121}
122
123impl fmt::Display for ElfReadError {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 use ElfReadError::*;
126 match self {
127 WrongReadSize(e, a) => {
128 write!(f, "expected to read {} bytes, but instead read {}", e, a)
129 }
130 SeekFromEndError(e) => write!(f, "couldn't seek from the end of the file: {}", e),
131 ReadFileError(e) => write!(f, "couldn't read from the file: {}", e),
132 OpenElfError(e) => write!(f, "couldn't open the elf file: {}", e),
133 ParseElfError(e) => write!(f, "couldn't parse the elf file: {}", e),
134 SectionRangeError => write!(f, "elf section pointed outside of the file"),
135 SectionNotAligned(s, a) => write!(f, "elf section {} had unaligned length {}", s, a),
136 FileSeekError(e) => write!(f, "couldn't seek in the output file: {}", e),
137 WriteSectionError(e) => write!(f, "couldn't write a section to the output file: {}", e),
138 }
139 }
140}
141
142#[allow(clippy::cognitive_complexity)]
143pub fn read_program<P: AsRef<Path>>(filename: P) -> Result<ProgramDescription, ElfReadError> {
144 let mut b = Vec::new();
145 {
146 let mut fi = File::open(filename).map_err(ElfReadError::OpenElfError)?;
147 fi.read_to_end(&mut b).map_err(ElfReadError::ReadFileError)?;
148 }
149 process_program(&b, false)
150}
151
152#[allow(clippy::cognitive_complexity)]
153pub fn read_loader<P: AsRef<Path>>(filename: P) -> Result<ProgramDescription, ElfReadError> {
154 let mut b = Vec::new();
155 {
156 let mut fi = File::open(filename).map_err(ElfReadError::OpenElfError)?;
157 fi.read_to_end(&mut b).map_err(ElfReadError::ReadFileError)?;
158 }
159 process_program(&b, true)
160}
161
162pub fn process_program(b: &[u8], rom_only: bool) -> Result<ProgramDescription, ElfReadError> {
163 let elf = ElfFile::new(&b).map_err(|x| ElfReadError::ParseElfError(x))?;
164 let entry_point = elf.header.pt2.entry_point() as u32;
165 let mut program_data = Cursor::new(Vec::new());
166 let mut poke_table = Vec::<(u32, u32)>::new();
167
168 let mut size = 0;
169 let mut data_offset = 0;
170 let mut data_size = 0;
171 let mut text_offset = 0;
172 let mut text_size = 0;
173 let mut bss_size = 0;
174 let mut phys_offset = 0;
175
176 debug!("ELF: {:?}", elf.header);
177 for ph in elf.program_iter() {
178 debug!("Program Header: {:?}", ph);
179 if ph.get_type() == Ok(ProgramType::Load) && phys_offset == 0 {
180 phys_offset = ph.physical_addr();
181 }
182 debug!("Physical address: {:08x}", ph.physical_addr());
183 debug!("Virtual address: {:08x}", ph.virtual_addr());
184 debug!("Offset: {:08x}", ph.offset());
185 debug!("Size: {:08x}", ph.file_size());
186 }
187 debug!("Program starts at 0x{:x}", entry_point);
188
189 let mut program_offset = 0;
190 let mut data_copy = Vec::new();
191 for s in elf.section_iter() {
192 let name = s.get_name(&elf).unwrap_or("<<error>>");
193
194 if s.address() == 0 {
195 debug!("(Skipping section {} -- invalid address)", name);
196 continue;
197 }
198
199 debug!("Section {}:", name);
200 debug!("Official header:");
201 debug!("{:x?}", s);
202 debug!("Interpreted:");
203 debug!(" flags: {:?}", s.flags());
204 debug!(" type: {:?}", s.get_type());
205 debug!(" address: {:08x}", s.address());
206 debug!(" offset: {:08x}", s.offset());
207 debug!(" size: {:x?}", s.size());
208 debug!(" link: {:?}", s.link());
209 size += s.size();
210 size += (4 - (size & 3)) & 3;
214 if size & 3 != 0 {
215 return Err(ElfReadError::SectionNotAligned(name.to_owned(), s.size() as usize));
216 }
217
218 if name == ".data" {
219 data_offset = s.address() as u32;
220 data_size += s.size() as u32;
221
222 if rom_only {
223 debug!(
224 "\n-- Not writing {}, type: {:?} flags: {:x}, len: {:x} -- ROM image requested --\n",
225 name,
226 s.get_type(),
227 s.flags(),
228 s.size(),
229 );
230
231 let section_data = s.raw_data(&elf);
235 if !section_data.iter().all(|&x| x == 0) {
236 data_copy.extend_from_slice(§ion_data);
265
266 }
282 continue;
283 }
284 } else if s.get_type() == Ok(ShType::NoBits) {
285 bss_size += s.size() as u32;
287 debug!("Skipping copy of {} @ {:08x} because nobits", name, s.address());
288 continue;
289 } else if text_offset == 0 && (s.address() != 0 || s.size() != 0) {
290 text_offset = s.address() as u32;
291 text_size += s.size() as u32;
292 } else {
293 if text_offset + text_size != s.address() as u32 {
294 let bytes_to_add = s.address() - (text_offset + text_size) as u64;
295 debug!("Padding text size by {} bytes...", bytes_to_add);
296 program_data
297 .seek(SeekFrom::Current(bytes_to_add as i64))
298 .map_err(ElfReadError::FileSeekError)?;
299 text_size += bytes_to_add as u32;
300 program_offset += bytes_to_add as u64;
301 }
308 text_size += s.size() as u32;
309 }
310 if s.size() == 0 {
311 debug!("Skipping {} because size is 0", name);
312 continue;
313 }
314 debug!("Adding {} to the file", name);
315 debug!(
316 " s.offset: {:08x} program_offset: {:08x} Bytes: {:08x}",
317 s.offset(),
318 program_offset,
319 s.raw_data(&elf).len(),
320 );
321 let section_data = s.raw_data(&elf);
322 debug!(
323 "Section start: {:02x} {:02x} {:02x} {:02x} going into offset 0x{:08x}",
324 section_data[0], section_data[1], section_data[2], section_data[3], program_offset
325 );
326 program_data.seek(SeekFrom::Start(program_offset)).map_err(ElfReadError::FileSeekError)?;
327 program_data.write(section_data).map_err(ElfReadError::WriteSectionError)?;
328 program_offset += section_data.len() as u64;
329 }
330 let observed_size = program_data.seek(SeekFrom::End(0)).map_err(ElfReadError::SeekFromEndError)?;
331
332 debug!("Text size: {} bytes", text_size);
333 debug!("Text offset: {:08x}", text_offset);
334 debug!("Data size: {} bytes", data_size);
335 debug!("Data offset: {:08x}", data_offset);
336 debug!("Program size: {} bytes", observed_size);
337
338 if data_offset as usize % size_of::<u32>() == 0 {
339 for (i, chunk) in data_copy.chunks_exact(4).enumerate() {
340 let word = u32::from_le_bytes(chunk.try_into().unwrap());
341 if word != 0 {
342 poke_table.push(((i as u32) * 4, word));
343 }
344 }
345 } else {
346 println!(
347 "Data section is not word-aligned, check objdump in detail for how to initialize the section"
348 );
349 }
350 Ok(ProgramDescription {
351 entry_point,
352 program: program_data.into_inner(),
353 data_size,
354 data_offset,
355 text_offset,
356 text_size,
357 bss_size,
358 clear_size: (((data_size + bss_size) as usize + size_of::<u32>() - 1) & !(size_of::<u32>() - 1))
360 as u32,
361 poke_table,
362 })
363}
364
365#[allow(clippy::cognitive_complexity)]
367pub fn read_minielf<P: AsRef<Path>>(filename: P) -> Result<MiniElf, ElfReadError> {
368 let mut b = Vec::new();
369 {
370 let mut fi = File::open(filename).map_err(ElfReadError::OpenElfError)?;
371 fi.read_to_end(&mut b).map_err(ElfReadError::ReadFileError)?;
372 }
373 process_minielf(&b)
374}
375
376pub fn process_minielf(b: &[u8]) -> Result<MiniElf, ElfReadError> {
377 let elf = ElfFile::new(&b).map_err(|x| ElfReadError::ParseElfError(x))?;
378 let entry_point = elf.header.pt2.entry_point() as u32;
379 let mut program_data = Cursor::new(Vec::new());
380 let mut alignment_offset = 0;
381
382 let mut sections = vec![];
383
384 debug!("ELF: {:?}", elf.header);
385 for ph in elf.program_iter() {
386 debug!("Program Header: {:?}", ph);
387 debug!("Physical address: {:08x}", ph.physical_addr());
388 debug!("Virtual address: {:08x}", ph.virtual_addr());
389 debug!("Offset: {:08x}", ph.offset());
390 debug!("Size: {:08x}", ph.file_size());
391 }
392 debug!("Program starts at 0x{:x}", entry_point);
393
394 let mut program_offset = 0;
396 let mut section_iter = elf.section_iter().peekable();
397 let mut init_offset = 0;
398 while let Some(s) = section_iter.next() {
399 let mut flags = MiniElfFlags::NONE;
400 let name = s.get_name(&elf).unwrap_or("<<error>>");
401
402 if s.address() == 0 {
403 debug!("(Skipping section {} -- invalid address)", name);
404 if init_offset == 0 {
406 init_offset = if let Some(s) = section_iter.peek() { s.offset() } else { 0 };
407 }
408 continue;
409 }
410 if alignment_offset == 0 {
411 alignment_offset = s.address() & 0xFFF;
412 }
413
414 debug!("Section {}:", name);
415 debug!("{} official header: {:x?}", name, s);
416 debug!("Interpreted:");
417 debug!(" flags: {:?}", s.flags());
418 debug!(" type: {:?}", s.get_type());
419 debug!(" address: {:08x}", s.address());
420 debug!(" offset: {:08x}", s.offset());
421 debug!(" size: {:?}", s.size());
422 debug!(" link: {:?}", s.link());
423 let mut size = s.size();
424
425 let no_copy = s.get_type() == Ok(ShType::NoBits);
426
427 if s.flags() & SHF_ALLOC == 0 {
428 debug!("section has no allocations -- skipping");
429 continue;
430 }
431 if no_copy {
432 flags |= MiniElfFlags::NOCOPY;
433 }
434 if s.flags() & SHF_EXECINSTR != 0 {
435 flags |= MiniElfFlags::EXECUTE;
436 }
437 if s.flags() & SHF_WRITE != 0 {
438 flags |= MiniElfFlags::WRITE;
439 }
440 if name == ".eh_frame_hdr" {
441 flags |= MiniElfFlags::EH_HEADER
442 } else if name == ".eh_frame" {
443 flags |= MiniElfFlags::EH_FRAME;
444 }
445
446 debug!("Adding {} to the file", name);
447 debug!(
448 "{} offset: {:08x} program_offset: {:08x} bytes: {} seek: {}",
449 name,
450 s.offset(),
451 program_offset,
452 if no_copy { 0 } else { s.raw_data(&elf).len() },
453 program_offset
454 );
455
456 if s.get_type() != Ok(ShType::NoBits) {
458 let section_data = s.raw_data(&elf);
459 let pad_amount = if let Some(next_section) = section_iter.peek() {
460 if (section_data.len() + program_offset as usize + init_offset as usize)
461 % next_section.align() as usize
462 != 0
463 {
464 let pad_amount = next_section.align() as usize
465 - ((section_data.len() + program_offset as usize + init_offset as usize)
466 % next_section.align() as usize);
467 if s.address() + size + pad_amount as u64 > next_section.address() {
468 (next_section.address() - (s.address() + size)) as usize
469 } else {
470 pad_amount
471 }
472 } else {
473 0
474 }
475 } else {
476 0
477 };
478
479 debug!(
480 "Section start: {:02x} {:02x} {:02x} {:02x} going into offset 0x{:08x}",
481 section_data[0], section_data[1], section_data[2], section_data[3], program_offset
482 );
483 program_data.seek(SeekFrom::Start(program_offset)).map_err(ElfReadError::FileSeekError)?;
484 program_data.write(section_data).map_err(ElfReadError::WriteSectionError)?;
485 program_offset += section_data.len() as u64;
486
487 if pad_amount != 0 {
488 let pad = vec![0u8; pad_amount];
489 program_data.write(&pad).map_err(ElfReadError::WriteSectionError)?;
490 program_offset += pad_amount as u64;
491 size += pad_amount as u64;
492 }
493 } else {
494 }
498 sections.push(MiniElfSection {
499 virt: s.address() as u32,
500 size: size as u32,
501 name: name.to_string(),
502 flags,
503 });
504 }
505 let observed_size = program_data.seek(SeekFrom::End(0)).map_err(ElfReadError::SeekFromEndError)?;
506
507 debug!("Program size: {} bytes", observed_size);
508 Ok(MiniElf {
509 entry_point,
510 sections,
511 program: program_data.into_inner(),
512 alignment_offset: alignment_offset as usize,
513 })
514}