| // Trivial elf segment loader, just copies segments straight from the in-memory | |
| // blob to the physical addresses specified in the program header. | |
| use core::cmp; | |
| use core::mem::transmute; | |
| use core::ptr; | |
| use core::slice; | |
| use crate::tar_loader; | |
| use matcha_hal::dprintf; | |
| pub const ELF_MAGIC: u32 = 0x464c457f; | |
| #[repr(C, packed)] | |
| #[derive(Debug, Copy, Clone)] | |
| pub struct Elf32Header { | |
| pub e_ident: [u8; 16], | |
| pub e_type: u16, /* Relocatable=1, Executable=2 (+ some more ..) */ | |
| pub e_machine: u16, /* Target architecture: MIPS=8 */ | |
| pub e_version: u32, /* Elf version (should be 1) */ | |
| pub e_entry: u32, /* Code entry point */ | |
| pub e_phoff: u32, /* Program header table */ | |
| pub e_shoff: u32, /* Section header table */ | |
| pub e_flags: u32, /* Flags */ | |
| pub e_ehsize: u16, /* ELF header size */ | |
| pub e_phentsize: u16, /* Size of one program segment header */ | |
| pub e_phnum: u16, /* Number of program segment headers */ | |
| pub e_shentsize: u16, /* Size of one section header */ | |
| pub e_shnum: u16, /* Number of section headers */ | |
| pub e_shstrndx: u16, /* Section header index of the string table for section | |
| header names */ | |
| } | |
| #[repr(C, packed)] | |
| #[derive(Debug, Copy, Clone)] | |
| pub struct Elf32Phdr { | |
| pub p_type: u32, /* Segment type: Loadable segment = 1 */ | |
| pub p_offset: u32, /* Offset of segment in file */ | |
| pub p_vaddr: u32, /* Reqd virtual address of segment when loading */ | |
| pub p_paddr: u32, /* Reqd physical address of segment (ignore) */ | |
| pub p_filesz: u32, /* How many bytes this segment occupies in file */ | |
| pub p_memsz: u32, /* How many bytes this segment should occupy in memory */ | |
| pub p_flags: u32, /* Flags: logical "or" of PF_ constants below */ | |
| pub p_align: u32, /* Reqd alignment of segment in memory */ | |
| } | |
| pub fn elf_phys_min(segments: &[Elf32Phdr]) -> u32 { | |
| return segments.iter().fold(0xFFFFFFFF, |acc, seg| { | |
| if seg.p_type != 1 { | |
| return acc; | |
| } else { | |
| return cmp::min(acc, seg.p_paddr); | |
| } | |
| }); | |
| } | |
| pub fn elf_phys_max(segments: &[Elf32Phdr]) -> u32 { | |
| return segments.iter().fold(0, |acc, seg| { | |
| if seg.p_type != 1 { | |
| return acc; | |
| } else { | |
| return cmp::max(acc, seg.p_paddr + seg.p_memsz); | |
| } | |
| }); | |
| } | |
| pub fn elf_virt_min(segments: &[Elf32Phdr]) -> u32 { | |
| return segments.iter().fold(0xFFFFFFFF, |acc, seg| { | |
| if seg.p_type != 1 { | |
| return acc; | |
| } else { | |
| return cmp::min(acc, seg.p_vaddr); | |
| } | |
| }); | |
| } | |
| pub fn elf_virt_max(segments: &[Elf32Phdr]) -> u32 { | |
| return segments.iter().fold(0, |acc, seg| { | |
| if seg.p_type != 1 { | |
| return acc; | |
| } else { | |
| return cmp::max(acc, seg.p_vaddr + seg.p_memsz); | |
| } | |
| }); | |
| } | |
| pub unsafe fn elf_get_segments(elf: *const Elf32Header) -> &'static [Elf32Phdr] { | |
| let base: *const u8 = transmute(elf); | |
| let seg_count = (*elf).e_phnum as usize; | |
| let segments: &[Elf32Phdr] = | |
| slice::from_raw_parts(transmute(base.offset((*elf).e_phoff as isize)), seg_count); | |
| return segments; | |
| } | |
| pub unsafe fn find_elf(name: &str) -> *const Elf32Header { | |
| let (tar_offset, tar_size) = tar_loader::find_file(name); | |
| if tar_size > 0 { | |
| let pmagic: *const u32 = transmute(tar_offset); | |
| if pmagic.read_volatile() == ELF_MAGIC { | |
| let elf_header: *const Elf32Header = transmute(tar_offset); | |
| return elf_header; | |
| } | |
| } | |
| dprintf!("Could not find elf for '{}'\n", name); | |
| return transmute(0); | |
| } | |
| pub unsafe fn load_elf_segments(elf: &Elf32Header, dst_offset: u32) { | |
| let base: *const u8 = transmute(elf); | |
| let seg_count = elf.e_phnum as usize; | |
| let segments: &[Elf32Phdr] = | |
| slice::from_raw_parts(transmute(base.offset(elf.e_phoff as isize)), seg_count); | |
| for i in 0..segments.len() { | |
| let seg = &segments[i]; | |
| // Loadable segment type. | |
| const PT_LOAD: u32 = 1; | |
| if seg.p_type != PT_LOAD { | |
| continue; | |
| } | |
| if seg.p_filesz == 0 { | |
| continue; | |
| } | |
| let src: *const u8 = transmute(base.offset(seg.p_offset as isize)); | |
| let txt_size = seg.p_filesz as usize; | |
| let txt_start: *mut u8 = transmute(seg.p_paddr + dst_offset); | |
| let txt_end = txt_start.offset(txt_size as isize); | |
| dprintf!( | |
| "seg {}: {:?} -> {:?}:{:?} ({} bytes)\n", | |
| i, | |
| src, | |
| txt_start, | |
| txt_end, | |
| txt_size | |
| ); | |
| ptr::copy_nonoverlapping(src, txt_start, txt_size); | |
| let bss_size = (seg.p_memsz - seg.p_filesz) as usize; | |
| let bss_start = txt_end; | |
| let bss_end = bss_start.offset(bss_size as isize); | |
| dprintf!( | |
| "bss {}: {:?} -> {:?}:{:?} ({} bytes)\n", | |
| i, | |
| src, | |
| bss_start, | |
| bss_end, | |
| bss_size | |
| ); | |
| ptr::write_bytes(bss_start, 0, bss_size as usize); | |
| } | |
| } |