extern crate alloc;
use alloc::vec::Vec;
use memory_addr::{PAGE_SIZE_4K, VirtAddr};
use page_table_entry::MappingFlags;
use crate::auxv::{AuxvEntry, AuxvType};
pub struct ELFPH {
pub offset: usize,
pub vaddr: VirtAddr,
pub memsz: u64,
pub filesz: u64,
pub flags: MappingFlags,
}
pub struct ELFParser<'a> {
elf: &'a xmas_elf::ElfFile<'a>,
base: usize,
}
impl<'a> ELFParser<'a> {
fn elf_base_addr(elf: &xmas_elf::ElfFile, interp_base: usize) -> Result<usize, &'static str> {
match elf.header.pt2.type_().as_type() {
xmas_elf::header::Type::Executable => Ok(0),
xmas_elf::header::Type::SharedObject => {
match elf
.program_iter()
.filter(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Interp))
.count()
{
0 => Ok(interp_base),
1 => Ok(0),
_ => Err("Multiple interpreters found"),
}
}
_ => Err("Unsupported ELF type"),
}
}
pub fn new(elf: &'a xmas_elf::ElfFile, interp_base: usize) -> Result<Self, &'static str> {
if elf.header.pt1.magic.as_slice() != b"\x7fELF" {
return Err("invalid elf!");
}
let base = Self::elf_base_addr(elf, interp_base)?;
Ok(Self { elf, base })
}
pub fn entry(&self) -> usize {
self.elf.header.pt2.entry_point() as usize + self.base
}
pub fn phnum(&self) -> usize {
self.elf.header.pt2.ph_count() as usize
}
pub fn phent(&self) -> usize {
self.elf.header.pt2.ph_entry_size() as usize
}
pub fn phdr(&self) -> usize {
self.elf.header.pt2.ph_offset() as usize + self.base
}
pub fn base(&self) -> usize {
self.base
}
pub fn auxv_vector(&self, pagesz: usize) -> [AuxvEntry; 17] {
[
AuxvEntry::new(AuxvType::PHDR, self.phdr()),
AuxvEntry::new(AuxvType::PHENT, self.phent()),
AuxvEntry::new(AuxvType::PHNUM, self.phnum()),
AuxvEntry::new(AuxvType::PAGESZ, pagesz),
AuxvEntry::new(AuxvType::BASE, self.base()),
AuxvEntry::new(AuxvType::FLAGS, 0),
AuxvEntry::new(AuxvType::ENTRY, self.entry()),
AuxvEntry::new(AuxvType::HWCAP, 0),
AuxvEntry::new(AuxvType::CLKTCK, 100),
AuxvEntry::new(AuxvType::PLATFORM, 0),
AuxvEntry::new(AuxvType::UID, 0),
AuxvEntry::new(AuxvType::EUID, 0),
AuxvEntry::new(AuxvType::GID, 0),
AuxvEntry::new(AuxvType::EGID, 0),
AuxvEntry::new(AuxvType::RANDOM, 0),
AuxvEntry::new(AuxvType::EXECFN, 0),
AuxvEntry::new(AuxvType::NULL, 0),
]
}
pub fn ph_load(&self) -> Vec<ELFPH> {
let mut segments = Vec::new();
self.elf
.program_iter()
.filter(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Load))
.for_each(|ph| {
let start_va = ph.virtual_addr() as usize + self.base;
let start_offset = ph.offset() as usize;
let mut flags = MappingFlags::USER;
if ph.flags().is_read() {
flags |= MappingFlags::READ;
}
if ph.flags().is_write() {
flags |= MappingFlags::WRITE;
}
if ph.flags().is_execute() {
flags |= MappingFlags::EXECUTE;
}
assert_eq!(start_va % PAGE_SIZE_4K, start_offset % PAGE_SIZE_4K);
let front_pad = start_va % PAGE_SIZE_4K;
segments.push(ELFPH {
offset: start_offset - front_pad,
vaddr: VirtAddr::from(start_va - front_pad),
memsz: ph.mem_size(),
filesz: ph.file_size(),
flags,
});
});
segments
}
}