use super::{LoadInfo, ELF_MAGIC};
use crate::io::UninitBuffer;
use crate::kernel::task::loader::aux_vec::{AuxKey, AuxVec};
use crate::path::Path;
use crate::{
io::ByteBuffer,
kernel::{
constants::ENOEXEC,
mem::{FileMapping, MMList, Mapping, Permission},
vfs::{dentry::Dentry, FsContext},
},
prelude::*,
};
use align_ext::AlignExt;
use alloc::vec::Vec;
use alloc::{ffi::CString, sync::Arc};
use eonix_mm::{
address::{Addr, AddrOps as _, VAddr},
paging::PAGE_SIZE,
};
use xmas_elf::{
header::{self, Class, HeaderPt1, Machine_, Type_},
program::{self, ProgramHeader32, ProgramHeader64},
P32, P64,
};
const INIT_STACK_SIZE: usize = 0x80_0000;
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct HeaderPt2
{
pub type_: Type_,
pub machine: Machine_,
pub version: u32,
pub entry_point: P,
pub ph_offset: P,
pub sh_offset: P,
pub flags: u32,
pub header_size: u16,
pub ph_entry_size: u16,
pub ph_count: u16,
pub sh_entry_size: u16,
pub sh_count: u16,
pub sh_str_index: u16,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct ElfHeader
{
pub pt1: HeaderPt1,
pub pt2: HeaderPt2
,
}
pub struct LdsoLoadInfo {
pub base: VAddr,
pub entry_ip: VAddr,
}
pub struct Elf32 {
pub file: Arc,
pub elf_header: ElfHeader,
pub program_headers: Vec,
}
impl Elf32 {
const DYN_BASE_ADDR: usize = 0x4000_0000;
const LDSO_BASE_ADDR: usize = 0xf000_0000;
const STACK_BASE_ADDR: usize = 0xffff_0000;
pub fn parse(elf_file: Arc) -> KResult {
let mut elf_header = UninitBuffer::>::new();
elf_file.read(&mut elf_header, 0)?;
let elf_header = elf_header.assume_init().map_err(|_| ENOEXEC)?;
let ph_offset = elf_header.pt2.ph_offset;
let ph_count = elf_header.pt2.ph_count;
let mut program_headers = vec![ProgramHeader32::default(); ph_count as usize];
elf_file.read(
&mut ByteBuffer::from(program_headers.as_mut_slice()),
ph_offset as usize,
)?;
Ok(Self {
file: elf_file,
elf_header,
program_headers,
})
}
fn is_shared_object(&self) -> bool {
self.elf_header.pt2.type_.as_type() == header::Type::SharedObject
}
fn entry_point(&self) -> u32 {
self.elf_header.pt2.entry_point
}
fn ph_count(&self) -> u16 {
self.elf_header.pt2.ph_count
}
fn ph_offset(&self) -> u32 {
self.elf_header.pt2.ph_offset
}
fn ph_entry_size(&self) -> u16 {
self.elf_header.pt2.ph_entry_size
}
fn ph_addr(&self) -> KResult {
let ph_offset = self.ph_offset();
for program_header in &self.program_headers {
if program_header.offset <= ph_offset
&& ph_offset < program_header.offset + program_header.file_size
{
return Ok(ph_offset - program_header.offset + program_header.virtual_addr);
}
}
Err(ENOEXEC)
}
pub fn load(&self, args: Vec, envs: Vec) -> KResult {
let mm_list = MMList::new();
// Load Segments
let (elf_base, data_segment_end) = self.load_segments(&mm_list)?;
// Load ldso(if have)
let ldso_load_info = self.load_ldso(&mm_list)?;
// Heap
mm_list.register_break(data_segment_end + 0x10000);
let aux_vec = Elf32::init_aux_vec(
self,
elf_base,
ldso_load_info
.as_ref()
.map(|ldso_load_info| ldso_load_info.base),
)?;
// Map stack
let sp = Elf32::create_and_init_stack(&mm_list, args, envs, aux_vec)?;
let entry_ip = if let Some(ldso_load_info) = ldso_load_info {
// Normal shared object(DYN)
ldso_load_info.entry_ip.into()
} else if self.is_shared_object() {
// ldso itself
elf_base + self.entry_point() as usize
} else {
// statically linked executable
(self.entry_point() as usize).into()
};
Ok(LoadInfo {
entry_ip,
sp,
mm_list,
})
}
fn init_aux_vec(
elf: &Elf32,
elf_base: VAddr,
ldso_base: Option,
) -> KResult> {
let mut aux_vec: AuxVec = AuxVec::new();
let ph_addr = if elf.is_shared_object() {
elf_base.addr() as u32 + elf.ph_addr()?
} else {
elf.ph_addr()?
};
aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as u32)?;
aux_vec.set(AuxKey::AT_PHDR, ph_addr)?;
aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u32)?;
aux_vec.set(AuxKey::AT_PHENT, elf.ph_entry_size() as u32)?;
let elf_entry = if elf.is_shared_object() {
elf_base.addr() as u32 + elf.entry_point()
} else {
elf.entry_point()
};
aux_vec.set(AuxKey::AT_ENTRY, elf_entry)?;
if let Some(ldso_base) = ldso_base {
aux_vec.set(AuxKey::AT_BASE, ldso_base.addr() as u32)?;
}
Ok(aux_vec)
}
pub fn load_segments(&self, mm_list: &MMList) -> KResult<(VAddr, VAddr)> {
let base: VAddr = if self.is_shared_object() {
Elf32::DYN_BASE_ADDR
} else {
0
}
.into();
let mut segments_end = VAddr::NULL;
for program_header in &self.program_headers {
let type_ = program_header.get_type().map_err(|_| ENOEXEC)?;
if type_ == program::Type::Load {
let segment_end = self.load_segment(program_header, mm_list, base)?;
if segment_end > segments_end {
segments_end = segment_end;
}
}
}
Ok((base, segments_end))
}
pub fn load_segment(
&self,
program_header: &ProgramHeader32,
mm_list: &MMList,
base_addr: VAddr,
) -> KResult {
let virtual_addr = base_addr + program_header.virtual_addr as usize;
let vmem_vaddr_end = virtual_addr + program_header.mem_size as usize;
let load_vaddr_end = virtual_addr + program_header.file_size as usize;
let vmap_start = virtual_addr.floor();
let vmem_len = vmem_vaddr_end.ceil() - vmap_start;
let file_len = load_vaddr_end.ceil() - vmap_start;
let file_offset = (program_header.offset as usize).align_down(PAGE_SIZE);
let permission = Permission {
read: program_header.flags.is_read(),
write: program_header.flags.is_write(),
execute: program_header.flags.is_execute(),
};
if file_len != 0 {
let real_file_length = load_vaddr_end - vmap_start;
mm_list.mmap_fixed(
vmap_start,
file_len,
Mapping::File(FileMapping::new(
self.file.clone(),
file_offset,
real_file_length,
)),
permission,
)?;
}
if vmem_len > file_len {
mm_list.mmap_fixed(
vmap_start + file_len,
vmem_len - file_len,
Mapping::Anonymous,
permission,
)?;
}
Ok(vmap_start + vmem_len)
}
pub fn load_ldso(&self, mm_list: &MMList) -> KResult