mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-30 09:01:44 -05:00
exec with interpreter
This commit is contained in:
@@ -13,7 +13,7 @@ const MMAP_BASE: usize = 0x4000_0000_0000;
|
||||
|
||||
/// Manages mappings in a process's address space.
|
||||
pub struct MemoryMap<AS: UserAddressSpace> {
|
||||
vmas: BTreeMap<VA, VMArea>,
|
||||
pub(super) vmas: BTreeMap<VA, VMArea>,
|
||||
address_space: AS,
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +48,21 @@ impl<AS: UserAddressSpace> ProcessVM<AS> {
|
||||
Ok(Self { mm, brk })
|
||||
}
|
||||
|
||||
pub fn from_map(map: MemoryMap<AS>, brk: VA) -> Self {
|
||||
pub fn from_map(map: MemoryMap<AS>) -> Self {
|
||||
// Last entry will be the VMA with the highest address.
|
||||
let brk = map
|
||||
.vmas
|
||||
.last_key_value()
|
||||
.expect("No VMAs in map")
|
||||
.1
|
||||
.region
|
||||
.end_address()
|
||||
// VMAs should already be page-aligned, but just in case.
|
||||
.align_up(PAGE_SIZE);
|
||||
|
||||
Self {
|
||||
mm: map,
|
||||
brk: VirtMemoryRegion::new(brk.align_up(PAGE_SIZE), 0),
|
||||
brk: VirtMemoryRegion::new(brk, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ impl VMAreaKind {
|
||||
/// managing a process's memory layout.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct VMArea {
|
||||
pub(super) region: VirtMemoryRegion,
|
||||
pub region: VirtMemoryRegion,
|
||||
pub(super) kind: VMAreaKind,
|
||||
pub(super) permissions: VMAPermissions,
|
||||
}
|
||||
@@ -205,11 +205,15 @@ impl VMArea {
|
||||
/// * `f`: A handle to the ELF file's inode.
|
||||
/// * `hdr`: The ELF program header (`LOAD` segment) to create the VMA from.
|
||||
/// * `endian`: The endianness of the ELF file, for correctly parsing header fields.
|
||||
/// * `address_bias`: A bias added to the VAs of the segment.
|
||||
pub fn from_pheader<E: Endian>(
|
||||
f: Arc<dyn Inode>,
|
||||
hdr: ProgramHeader64<E>,
|
||||
endian: E,
|
||||
address_bias: Option<usize>,
|
||||
) -> VMArea {
|
||||
let address_bias = address_bias.unwrap_or(0);
|
||||
|
||||
let mut permissions = VMAPermissions {
|
||||
read: false,
|
||||
write: false,
|
||||
@@ -229,7 +233,7 @@ impl VMArea {
|
||||
}
|
||||
|
||||
let mappable_region = VirtMemoryRegion::new(
|
||||
VA::from_value(hdr.p_vaddr(endian) as usize),
|
||||
VA::from_value(hdr.p_vaddr(endian) as usize + address_bias),
|
||||
hdr.p_memsz(endian) as usize,
|
||||
)
|
||||
.to_mappable_region();
|
||||
@@ -446,6 +450,11 @@ impl VMArea {
|
||||
VMAreaKind::Anon => new_vma,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the virtual memory region managed by this VMA.
|
||||
pub fn region(&self) -> VirtMemoryRegion {
|
||||
self.region
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -17,7 +17,6 @@ const PROT_READ: u64 = 1;
|
||||
const PROT_WRITE: u64 = 2;
|
||||
const PROT_EXEC: u64 = 4;
|
||||
|
||||
const MAP_FILE: u64 = 0x0000;
|
||||
const MAP_SHARED: u64 = 0x0001;
|
||||
const MAP_PRIVATE: u64 = 0x0002;
|
||||
const MAP_FIXED: u64 = 0x0010;
|
||||
@@ -86,9 +85,10 @@ pub async fn sys_mmap(
|
||||
|
||||
let requested_len = len as usize;
|
||||
|
||||
let kind = if flags & (MAP_ANON | MAP_ANONYMOUS) != 0 {
|
||||
let kind = if (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0 {
|
||||
VMAreaKind::Anon
|
||||
} else if flags == MAP_FILE {
|
||||
} else {
|
||||
// File-backed mapping: require a valid fd and use the provided offset.
|
||||
let fd = current_task()
|
||||
.fd_table
|
||||
.lock_save_irq()
|
||||
@@ -98,9 +98,6 @@ pub async fn sys_mmap(
|
||||
let inode = fd.inode().ok_or(KernelError::BadFd)?;
|
||||
|
||||
VMAreaKind::new_file(inode, offset, len)
|
||||
} else {
|
||||
// One of MAP_FILE or MAP_ANONYMOUS must be set.
|
||||
return Err(KernelError::InvalidValue);
|
||||
};
|
||||
|
||||
let address_request = if addr.is_null() {
|
||||
|
||||
@@ -31,6 +31,8 @@ use libkernel::{
|
||||
region::VirtMemoryRegion,
|
||||
},
|
||||
};
|
||||
use object::Endian;
|
||||
use object::elf::{ET_DYN, ProgramHeader64};
|
||||
use object::{
|
||||
LittleEndian,
|
||||
elf::{self, PT_LOAD},
|
||||
@@ -39,10 +41,44 @@ use object::{
|
||||
|
||||
mod auxv;
|
||||
|
||||
const LINKER_BIAS: usize = 0x0000_7000_0000_0000;
|
||||
const PROG_BIAS: usize = 0x0000_5000_0000_0000;
|
||||
|
||||
const STACK_END: usize = 0x0000_8000_0000_0000;
|
||||
const STACK_SZ: usize = 0x2000 * 0x400;
|
||||
const STACK_START: usize = STACK_END - STACK_SZ;
|
||||
|
||||
/// Process a set of progream headers from an ELF. Create VMAs for all `PT_LOAD`
|
||||
/// segments, optionally applying `bias` to the load address.
|
||||
///
|
||||
/// If a VMA was found that contains the headers themselves, the address of the
|
||||
/// *VMA* is returned.
|
||||
fn process_prog_headers<E: Endian>(
|
||||
hdrs: &[ProgramHeader64<E>],
|
||||
vmas: &mut Vec<VMArea>,
|
||||
bias: Option<usize>,
|
||||
elf_file: Arc<dyn Inode>,
|
||||
endian: E,
|
||||
) -> Option<VA> {
|
||||
let mut hdr_addr = None;
|
||||
|
||||
for hdr in hdrs {
|
||||
if hdr.p_type(endian) == PT_LOAD {
|
||||
let vma = VMArea::from_pheader(elf_file.clone(), *hdr, endian, bias);
|
||||
|
||||
// Find PHDR: Assumption segment with p_offset == 0 contains
|
||||
// headers.
|
||||
if hdr.p_offset.get(endian) == 0 {
|
||||
hdr_addr = Some(vma.region().start_address());
|
||||
}
|
||||
|
||||
vmas.push(vma);
|
||||
}
|
||||
}
|
||||
|
||||
hdr_addr
|
||||
}
|
||||
|
||||
pub async fn kernel_exec(
|
||||
inode: Arc<dyn Inode>,
|
||||
argv: Vec<String>,
|
||||
@@ -69,7 +105,7 @@ pub async fn kernel_exec(
|
||||
|
||||
// Detect PT_INTERP (dynamic linker) if present
|
||||
let mut interp_path: Option<String> = None;
|
||||
for hdr in hdrs {
|
||||
for hdr in hdrs.iter() {
|
||||
if hdr.p_type(endian) == elf::PT_INTERP {
|
||||
let off = hdr.p_offset(endian) as usize;
|
||||
let filesz = hdr.p_filesz(endian) as usize;
|
||||
@@ -87,46 +123,45 @@ pub async fn kernel_exec(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(path) = interp_path {
|
||||
panic!("Dynamic linker not supported yet: {}", path);
|
||||
// return exec_with_interp(inode, &elf, endian, &ph_buf, &hdrs, path, argv, envp).await;
|
||||
}
|
||||
// Setup a program bias for PIE.
|
||||
let main_bias = if elf.e_type.get(endian) == ET_DYN {
|
||||
Some(PROG_BIAS)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// static ELF ...
|
||||
let mut auxv = Vec::new();
|
||||
|
||||
// Push program header params (for the main executable)
|
||||
auxv.push(AT_PHNUM);
|
||||
auxv.push(elf.e_phnum.get(endian) as _);
|
||||
auxv.push(AT_PHENT);
|
||||
auxv.push(elf.e_phentsize(endian) as _);
|
||||
let mut auxv = vec![
|
||||
AT_PHNUM,
|
||||
elf.e_phnum.get(endian) as _,
|
||||
AT_PHENT,
|
||||
elf.e_phentsize(endian) as _,
|
||||
];
|
||||
|
||||
let mut vmas = Vec::new();
|
||||
let mut highest_addr = 0;
|
||||
|
||||
for hdr in hdrs {
|
||||
let kind = hdr.p_type(endian);
|
||||
|
||||
if kind == PT_LOAD {
|
||||
vmas.push(VMArea::from_pheader(inode.clone(), *hdr, endian));
|
||||
|
||||
if hdr.p_offset.get(endian) == 0 {
|
||||
// TODO: potentially more validation that this VA will contain
|
||||
// the program headers.
|
||||
auxv.push(AT_PHDR);
|
||||
auxv.push(hdr.p_vaddr.get(endian) + elf.e_phoff.get(endian));
|
||||
}
|
||||
|
||||
let mapping_end = hdr.p_vaddr(endian) + hdr.p_memsz(endian);
|
||||
|
||||
if mapping_end > highest_addr {
|
||||
highest_addr = mapping_end;
|
||||
}
|
||||
}
|
||||
// Process the binary progream headers.
|
||||
if let Some(hdr_addr) = process_prog_headers(hdrs, &mut vmas, main_bias, inode.clone(), endian)
|
||||
{
|
||||
auxv.push(AT_PHDR);
|
||||
auxv.push(hdr_addr.add_bytes(elf.e_phoff(endian) as _).value() as _);
|
||||
}
|
||||
|
||||
let main_entry = VA::from_value(elf.e_entry(endian) as usize + main_bias.unwrap_or(0));
|
||||
|
||||
// AT_ENTRY is the same in the static and interp case.
|
||||
auxv.push(AT_ENTRY);
|
||||
auxv.push(elf.e_entry(endian) as u64);
|
||||
auxv.push(main_entry.value() as _);
|
||||
|
||||
let entry_addr = if let Some(path) = interp_path {
|
||||
auxv.push(AT_BASE);
|
||||
auxv.push(LINKER_BIAS as _);
|
||||
|
||||
// Returns the entry address of the interp program.
|
||||
process_interp(path, &mut vmas).await?
|
||||
} else {
|
||||
// Otherwise, it's just the binary itself.
|
||||
main_entry
|
||||
};
|
||||
|
||||
vmas.push(VMArea::new(
|
||||
VirtMemoryRegion::new(VA::from_value(STACK_START), STACK_SZ),
|
||||
@@ -135,12 +170,10 @@ pub async fn kernel_exec(
|
||||
));
|
||||
|
||||
let mut mem_map = MemoryMap::from_vmas(vmas)?;
|
||||
|
||||
let stack_ptr = setup_user_stack(&mut mem_map, &argv, &envp, auxv)?;
|
||||
|
||||
let user_ctx =
|
||||
ArchImpl::new_user_context(VA::from_value(elf.e_entry(endian) as usize), stack_ptr);
|
||||
let mut vm = ProcessVM::from_map(mem_map, VA::from_value(highest_addr as usize));
|
||||
let user_ctx = ArchImpl::new_user_context(entry_addr, stack_ptr);
|
||||
let mut vm = ProcessVM::from_map(mem_map);
|
||||
|
||||
// We don't have to worry about actually calling for a full context switch
|
||||
// here. Parts of the old process that are replaced will go out of scope and
|
||||
@@ -274,6 +307,39 @@ fn setup_user_stack(
|
||||
Ok(VA::from_value(final_sp_val))
|
||||
}
|
||||
|
||||
// Dynamic linker path: map PT_INTERP interpreter and return start address of
|
||||
// the interpreter program.
|
||||
async fn process_interp(interp_path: String, vmas: &mut Vec<VMArea>) -> Result<VA> {
|
||||
// Resolve interpreter path from root; this assumes interp_path is absolute.
|
||||
let task = current_task_shared();
|
||||
let path = Path::new(&interp_path);
|
||||
let interp_inode = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
|
||||
|
||||
// Parse interpreter ELF header
|
||||
let mut hdr_buf = [0u8; core::mem::size_of::<elf::FileHeader64<LittleEndian>>()];
|
||||
interp_inode.read_at(0, &mut hdr_buf).await?;
|
||||
let interp_elf = elf::FileHeader64::<LittleEndian>::parse(&hdr_buf[..])
|
||||
.map_err(|_| ExecError::InvalidElfFormat)?;
|
||||
let iendian = interp_elf.endian().unwrap();
|
||||
|
||||
// Read interpreter program headers
|
||||
let interp_ph_table_size = interp_elf.e_phnum.get(iendian) as usize
|
||||
* interp_elf.e_phentsize.get(iendian) as usize
|
||||
+ interp_elf.e_phoff.get(iendian) as usize;
|
||||
let mut interp_ph_buf = vec![0u8; interp_ph_table_size];
|
||||
interp_inode.read_at(0, &mut interp_ph_buf).await?;
|
||||
let interp_hdrs = interp_elf
|
||||
.program_headers(iendian, &interp_ph_buf[..])
|
||||
.map_err(|_| ExecError::InvalidPHdrFormat)?;
|
||||
|
||||
// Build VMAs for interpreter
|
||||
process_prog_headers(interp_hdrs, vmas, Some(LINKER_BIAS), interp_inode, iendian);
|
||||
|
||||
let interp_entry = VA::from_value(LINKER_BIAS + interp_elf.e_entry(iendian) as usize);
|
||||
|
||||
Ok(interp_entry)
|
||||
}
|
||||
|
||||
pub async fn sys_execve(
|
||||
path: TUA<c_char>,
|
||||
mut usr_argv: TUA<TUA<c_char>>,
|
||||
|
||||
Reference in New Issue
Block a user