blob: 219fd63efc7bafb9af29de60d847a96c46ac2071 [file] [log] [blame]
// 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);
}
}