use crate::cap::Capability; use crate::debug; use crate::init; use crate::syscall; use crate::zion::ZError; use alloc::fmt::Debug; const ELF_MAGIC: u32 = 0x464C457F; const ELF_IDENT_32BIT: u8 = 0x1; const ELF_IDENT_64BIT: u8 = 0x2; const ELF_ENDIAN_LITTLE: u8 = 0x1; const ELF_ENDIAN_BIG: u8 = 0x2; const ELF_VERSION_CURRENT: u8 = 0x1; const ELF_ABI_SYSV: u8 = 0x0; const ELF_ABI_LINUX: u8 = 0x3; const ELF_FILE_RELOC: u16 = 0x1; const ELF_FILE_EXEC: u16 = 0x2; const ELF_FILE_DYN: u16 = 0x3; const ELF_FILE_CORE: u16 = 0x4; const ELF_MACH_X86: u16 = 0x3; const ELF_MACH_AMD64: u16 = 0x3e; #[repr(C, packed(1))] // Header spec from wikipedia: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#ELF_header struct Elf64Header { // 0x7F454C46 (0x7F followed by 'ELF') magic: u32, // 1 for 32 bit, 2 for 64 bit. elf_class: u8, // 1 for little, 2 for big. endianess: u8, // Current version is 1. ident_version: u8, // Target OS abi. abi: u8, abi_version: u8, ident_padding: [u8; 7], file_type: u16, machine: u16, version: u32, entry: u64, program_header_offset: u64, section_header_offset: u64, flags: u32, header_size: u16, program_header_entry_size: u16, program_header_count: u16, section_header_entry_size: u16, section_header_entry_number: u16, section_header_str_index: u16, } impl Debug for Elf64Header { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let magic = self.magic; let elf_class = match self.elf_class { ELF_IDENT_32BIT => "32 bit", ELF_IDENT_64BIT => "64 bit", _ => "Unknown", }; let endianess = match self.endianess { ELF_ENDIAN_LITTLE => "Little", ELF_ENDIAN_BIG => "Big", _ => "Unknown", }; let ident_version = self.ident_version; let version = self.version; f.write_fmt(format_args!( "ELF Header Magic: {:#x}, Class: {}, Endianess: {}, Version (ident): {}, Version: {}\n", magic, elf_class, endianess, ident_version, version, ))?; let abi = match self.abi { ELF_ABI_SYSV => "SYSV", ELF_ABI_LINUX => "Linux", _ => "Unknown", }; let abi_version = self.abi_version; f.write_fmt(format_args!("\tABI: {}, Version: {}\n", abi, abi_version))?; let file_type = match self.file_type { ELF_FILE_EXEC => "Executable", ELF_FILE_RELOC => "Relocatable", ELF_FILE_DYN => "Shared Obj", ELF_FILE_CORE => "Core file", _ => "Unknown", }; let machine = match self.machine { ELF_MACH_X86 => "x86 (32bit)", ELF_MACH_AMD64 => "x86-64", _ => "Unknown", }; let entry_point = self.entry; f.write_fmt(format_args!( "\tFile type: {}, Machine Arch: {}, Entry point {:#x}", file_type, machine, entry_point )) } } fn validate_header(elf_header: &Elf64Header) -> Result<(), ZError> { if elf_header.magic != ELF_MAGIC { let magic = elf_header.magic; debug!( "Elf header incorrect got {:#x} expected {:#x}", magic, ELF_MAGIC ); return Err(ZError::INVALID_ARGUMENT); } if elf_header.elf_class != ELF_IDENT_64BIT { let class = elf_header.elf_class; debug!( "Elf class must be {} for 64 bit, got: {}", ELF_IDENT_64BIT, class ); return Err(ZError::INVALID_ARGUMENT); } if elf_header.endianess != ELF_ENDIAN_LITTLE { let endianess = elf_header.endianess; debug!( "Elf endianess must be {} for little, got: {}", ELF_ENDIAN_LITTLE, endianess ); return Err(ZError::INVALID_ARGUMENT); } if elf_header.ident_version != ELF_VERSION_CURRENT { let version = elf_header.ident_version; debug!( "Elf version (ident) must be {}, got: {}", ELF_VERSION_CURRENT, version ); return Err(ZError::INVALID_ARGUMENT); } if elf_header.file_type != ELF_FILE_EXEC { let file_type = elf_header.file_type; debug!( "Elf file type must be {} for executable, got {:x}", ELF_FILE_EXEC, file_type ); return Err(ZError::INVALID_ARGUMENT); } Ok(()) } const ELF_PROG_NULL: u32 = 0x0; const ELF_PROG_LOAD: u32 = 0x1; const ELF_PROG_DYNAMIC: u32 = 0x2; const ELF_PROG_INTERP: u32 = 0x3; const ELF_PROG_NOTE: u32 = 0x4; const ELF_PROG_SHLIB: u32 = 0x5; const ELF_PROG_PTHDR: u32 = 0x6; const ELF_PROG_THREAD_LOCAL: u32 = 0x7; // Stack unwind tables. const ELF_PROG_GNU_EH_FRAME: u32 = 0x6474e550; const ELF_PROG_GNU_STACK: u32 = 0x6474e551; const ELF_PROG_GNU_RELRO: u32 = 0x6474e552; #[repr(C, packed(1))] struct Elf64ProgramHeader { prog_type: u32, flags: u32, offset: u64, vaddr: u64, paddr: u64, file_size: u64, mem_size: u64, align: u64, } impl Debug for Elf64ProgramHeader { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let prog_type = match self.prog_type { ELF_PROG_NULL => "NULL", ELF_PROG_LOAD => "LOAD", ELF_PROG_DYNAMIC => "DYNAMIC", ELF_PROG_INTERP => "INTERP", ELF_PROG_NOTE => "NOTE", ELF_PROG_SHLIB => "SHARED LIB", ELF_PROG_PTHDR => "PROG TABLE HEADER", ELF_PROG_THREAD_LOCAL => "THREAD LOCAL", ELF_PROG_GNU_EH_FRAME => "GNU EH FRAME", ELF_PROG_GNU_RELRO => "GNU RELOCATABLE", ELF_PROG_GNU_STACK => "GNU STACK", _ => "UNKNOWN", }; let offset = self.offset; let vaddr = self.vaddr; let paddr = self.paddr; let file_size = self.file_size; let mem_size = self.mem_size; let align = self.align; f.write_fmt(format_args!( "Type: {}, offset: {:#x}, vaddr: {:#x}, paddr: {:#x}, file_size: {:#x}, mem_size: {:#x}, align: {:#x}", prog_type, offset, vaddr, paddr, file_size, mem_size, align )) } } fn load_program_segment( prog_header: &Elf64ProgramHeader, file: &[u8], vmas: &Capability, ) -> Result<(), ZError> { debug!("{:?}", prog_header); match prog_header.prog_type { ELF_PROG_NULL | ELF_PROG_NOTE | ELF_PROG_PTHDR | ELF_PROG_GNU_STACK | ELF_PROG_GNU_RELRO | ELF_PROG_GNU_EH_FRAME => Ok(()), ELF_PROG_LOAD => { let page_offset = prog_header.vaddr & 0xFFF; let mem_size = page_offset + prog_header.mem_size; let mem_object = crate::mem::MemoryRegion::new(mem_size)?; for i in mem_object.mut_slice() { *i = 0; } let file_start = prog_header.offset as usize; let file_end = file_start + prog_header.file_size as usize; let from_slice = &file[file_start..file_end]; let mem_start = page_offset as usize; let mem_end = mem_start + (prog_header.file_size as usize); let to_slice: &mut [u8] = &mut mem_object.mut_slice()[mem_start..mem_end]; to_slice.copy_from_slice(from_slice); let vaddr = prog_header.vaddr - page_offset; syscall::address_space_map_external(vmas, mem_object.cap(), vaddr) } ELF_PROG_DYNAMIC => { debug!("Unimplemented dynamic elf sections."); Err(ZError::UNIMPLEMENTED) } ELF_PROG_INTERP => { debug!("Unimplemented interpreter elf sections."); Err(ZError::UNIMPLEMENTED) } ELF_PROG_SHLIB => { debug!("Unimplemented shared lib elf sections."); Err(ZError::UNIMPLEMENTED) } ELF_PROG_THREAD_LOCAL => { debug!("Unimplemented thread local elf sections."); Err(ZError::UNIMPLEMENTED) } _ => { let prog_type = prog_header.prog_type; debug!("Unknown elf program header type: {:#x}", prog_type); Err(ZError::UNKNOWN) } } } fn load_elf_program(elf_file: &[u8], vmas: &Capability) -> Result { assert!(elf_file.len() > size_of::()); let header: &Elf64Header = unsafe { elf_file .as_ptr() .cast::() .as_ref() .ok_or(ZError::NULL_PTR)? }; debug!("{:?}", header); validate_header(header)?; for prog_ind in 0..header.program_header_count { let prog_header_offset = header.program_header_offset + ((prog_ind as u64) * (header.program_header_entry_size as u64)); let prog_header_end = prog_header_offset + header.program_header_entry_size as u64; let prog_header_slice = &elf_file[prog_header_offset as usize..prog_header_end as usize]; let prog_header: &Elf64ProgramHeader = unsafe { prog_header_slice .as_ptr() .cast::() .as_ref() .ok_or(ZError::NULL_PTR)? }; load_program_segment(prog_header, elf_file, vmas)?; } Ok(header.entry) } pub fn spawn_process_from_elf_and_init( elf_file: &[u8], init_cap: Capability, ) -> Result { let self_cap = Capability::take_copy(unsafe { init::SELF_PROC_CAP })?; let port_cap = syscall::port_create()?; let (new_proc_cap, new_as_cap, foreign_port_id) = syscall::process_spawn(&self_cap, port_cap.duplicate(Capability::PERMS_ALL)?)?; let entry_point = load_elf_program(elf_file, &new_as_cap)?; let port = crate::port::PortClient::take_from(port_cap); port.write_u64_and_cap( crate::init::Z_INIT_SELF_PROC, new_proc_cap.duplicate(Capability::PERMS_ALL)?, )?; port.write_u64_and_cap(crate::init::Z_INIT_SELF_VMAS, new_as_cap)?; port.write_u64_and_cap(crate::init::Z_INIT_ENDPOINT, init_cap)?; let thread_cap = syscall::thread_create(&new_proc_cap)?; syscall::thread_start(&thread_cap, entry_point, foreign_port_id, 0)?; Ok(new_proc_cap) } pub fn spawn_process_from_elf(elf_file: &[u8]) -> Result { let yellowstone = Capability::take_copy(unsafe { crate::init::INIT_ENDPOINT })?; spawn_process_from_elf_and_init(elf_file, yellowstone) }