From 896910b44a33181cc3e2790b0c84f5b0729a9feb Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Tue, 7 Mar 2023 17:13:35 +0800 Subject: [PATCH] load elf from file --- src/apps/execve/execve | 2 +- src/apps/execve/execve.c | 5 +- src/build.rs | 1 - src/services/libs/jinux-std/src/lib.rs | 23 +- src/services/libs/jinux-std/src/prelude.rs | 2 + .../libs/jinux-std/src/process/clone.rs | 4 +- .../jinux-std/src/process/elf/elf_file.rs | 36 ++ .../src/process/elf/elf_segment_pager.rs | 56 ++-- .../jinux-std/src/process/elf/init_stack.rs | 27 +- .../jinux-std/src/process/elf/load_elf.rs | 312 +++++------------- .../libs/jinux-std/src/process/elf/mod.rs | 32 +- .../libs/jinux-std/src/process/mod.rs | 109 +----- .../src/process/posix_thread/name.rs | 3 +- .../process/posix_thread/posix_thread_ext.rs | 30 +- .../libs/jinux-std/src/process/rlimit.rs | 2 +- .../libs/jinux-std/src/process/signal/mod.rs | 2 +- .../libs/jinux-std/src/syscall/execve.rs | 22 +- .../libs/jinux-std/src/syscall/readlink.rs | 2 +- src/services/libs/jinux-std/src/user_apps.rs | 53 +-- 19 files changed, 265 insertions(+), 458 deletions(-) diff --git a/src/apps/execve/execve b/src/apps/execve/execve index a5403b6a9..143ed4450 100755 --- a/src/apps/execve/execve +++ b/src/apps/execve/execve @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c04094e4c0da36e9f8cc5059f540dbc2123d5e4caa1d0b7427577ee734710b23 +oid sha256:c7cc90df87ade7ff2cb494e13678aceaf93542883558fda947bd2fc01e2d73a6 size 871952 diff --git a/src/apps/execve/execve.c b/src/apps/execve/execve.c index 13258ac62..6d7ac44d1 100644 --- a/src/apps/execve/execve.c +++ b/src/apps/execve/execve.c @@ -4,10 +4,11 @@ int main() { char* argv[] = { "argv1", "argv2", NULL }; char* envp[] = { "home=/", "version=1.1", NULL }; - printf("Execve a new file ./hello:\n"); + // The hello will be put at /execve/hello in InitRamfs + printf("Execve a new file /execve/hello:\n"); // flush the stdout content to ensure the content print to console fflush(stdout); - execve("./hello", argv, envp); + execve("/execve/hello", argv, envp); printf("Should not print\n"); fflush(stdout); return 0; diff --git a/src/build.rs b/src/build.rs index 88a466e2a..3e56c1821 100644 --- a/src/build.rs +++ b/src/build.rs @@ -6,7 +6,6 @@ fn main() -> Result<(), Box> { } fn limine_build_script() -> Result<(), Box> { - // Have cargo rerun this script if the linker script or CARGO_PKG_ENV changes. println!("cargo:rerun-if-changed=boot/limine/conf/linker.ld"); println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME"); diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs index a045704c5..daf62222c 100644 --- a/src/services/libs/jinux-std/src/lib.rs +++ b/src/services/libs/jinux-std/src/lib.rs @@ -67,18 +67,31 @@ pub fn init_thread() { "[jinux-std/lib.rs] spawn kernel thread, tid = {}", thread.tid() ); - + + // FIXME: should be running this apps before we running shell? + println!(""); + println!("[kernel] Running test programs"); + println!(""); + // Run test apps + for app in get_all_apps().unwrap().into_iter() { + let UserApp { + executable_path: app_name, + argv, + envp, + } = app; + println!("[jinux-std/lib.rs] spwan {:?} process", app_name); + Process::spawn_user_process(app_name.clone(), argv, Vec::new()); + } + // Run busybox ash let UserApp { - elf_path: app_name, - app_content, + executable_path: app_name, argv, envp, } = get_busybox_app().unwrap(); - let app_content = app_content.into_boxed_slice(); println!(""); println!("BusyBox v1.35.0 built-in shell (ash)\n"); - Process::spawn_user_process(app_name.clone(), Box::leak(app_content), argv, Vec::new()); + Process::spawn_user_process(app_name.clone(), argv, Vec::new()); loop { // We don't have preemptive scheduler now. diff --git a/src/services/libs/jinux-std/src/prelude.rs b/src/services/libs/jinux-std/src/prelude.rs index 41dc91ed9..4f5dc8d5a 100644 --- a/src/services/libs/jinux-std/src/prelude.rs +++ b/src/services/libs/jinux-std/src/prelude.rs @@ -6,6 +6,8 @@ pub(crate) use alloc::collections::BTreeSet; pub(crate) use alloc::collections::LinkedList; pub(crate) use alloc::collections::VecDeque; pub(crate) use alloc::ffi::CString; +pub(crate) use alloc::string::String; +pub(crate) use alloc::string::ToString; pub(crate) use alloc::sync::Arc; pub(crate) use alloc::sync::Weak; pub(crate) use alloc::vec; diff --git a/src/services/libs/jinux-std/src/process/clone.rs b/src/services/libs/jinux-std/src/process/clone.rs index 57b268197..8255e6864 100644 --- a/src/services/libs/jinux-std/src/process/clone.rs +++ b/src/services/libs/jinux-std/src/process/clone.rs @@ -218,8 +218,8 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res // clone system V semaphore clone_sysvsem(clone_flags)?; - let child_elf_path = current.filename().unwrap().clone(); - let child_thread_name = ThreadName::new_from_elf_path(&child_elf_path)?; + let child_elf_path = current.executable_path().unwrap().clone(); + let child_thread_name = ThreadName::new_from_executable_path(&child_elf_path)?; // inherit parent's sig mask let current_thread = current_thread!(); diff --git a/src/services/libs/jinux-std/src/process/elf/elf_file.rs b/src/services/libs/jinux-std/src/process/elf/elf_file.rs index fdceb81f1..eee665337 100644 --- a/src/services/libs/jinux-std/src/process/elf/elf_file.rs +++ b/src/services/libs/jinux-std/src/process/elf/elf_file.rs @@ -45,6 +45,42 @@ impl Elf { program_headers, }) } + + // The following info is used to setup init stack + /// the entry point of the elf + pub fn entry_point(&self) -> Vaddr { + self.elf_header.pt2.entry_point as Vaddr + } + /// page header table offset + pub fn ph_off(&self) -> u64 { + self.elf_header.pt2.ph_offset + } + /// number of program headers + pub fn ph_count(&self) -> u16 { + self.elf_header.pt2.ph_count + } + /// The size of a program header + pub fn ph_ent(&self) -> u16 { + self.elf_header.pt2.ph_entry_size + } + + /// The virtual addr of program headers table address + pub fn ph_addr(&self) -> Result { + let ph_offset = self.ph_off(); + 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) as Vaddr, + ); + } + } + return_errno_with_message!( + Errno::ENOEXEC, + "can not find program header table address in elf" + ); + } } pub struct ElfHeader { diff --git a/src/services/libs/jinux-std/src/process/elf/elf_segment_pager.rs b/src/services/libs/jinux-std/src/process/elf/elf_segment_pager.rs index 6bb0afe47..17317c791 100644 --- a/src/services/libs/jinux-std/src/process/elf/elf_segment_pager.rs +++ b/src/services/libs/jinux-std/src/process/elf/elf_segment_pager.rs @@ -1,36 +1,47 @@ +use core::ops::Range; + +use crate::fs::file_handle::FileHandle; +use crate::fs::utils::SeekFrom; use crate::prelude::*; use crate::vm::vmar::{get_intersected_range, is_intersected}; use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo}; use jinux_frame::AlignExt; +use xmas_elf::program::ProgramHeader64; use crate::vm::vmo::Pager; -use super::load_elf::ElfSegment; +// use super::load_elf::ElfSegment; /// The pager behind a elf segment pub struct ElfSegmentPager { /// The pager size pager_size: usize, - /// data for current segment - segment_data: &'static [u8], + /// the back up file + file: Arc, + /// The segment offset in backup file + file_offset: usize, + /// The segment size in backup file + file_size: usize, /// The offset for the segment data. /// The pager always starts at page-align address, while the segment data may start at any address. /// So the offset will be the segment data start address % PAGE_SIZE - offset: usize, + page_offset: usize, } impl ElfSegmentPager { - pub fn new(elf_file_content: &'static [u8], elf_segment: &ElfSegment) -> Self { - let start = elf_segment.start_address().align_down(PAGE_SIZE); - let end = elf_segment.end_address().align_up(PAGE_SIZE); + pub fn new(file: Arc, program_header: &ProgramHeader64) -> Self { + let ph_start = program_header.virtual_addr as Vaddr; + let ph_end = ph_start + program_header.mem_size as Vaddr; + let start = ph_start.align_down(PAGE_SIZE); + let end = ph_end.align_up(PAGE_SIZE); let pager_size = end - start; - let offset = elf_segment.start_address() % PAGE_SIZE; - let elf_file_segment = - &elf_file_content[elf_segment.offset..elf_segment.offset + elf_segment.file_size]; + let offset = ph_start % PAGE_SIZE; Self { pager_size, - segment_data: elf_file_segment, - offset, + file, + file_offset: program_header.offset as usize, + file_size: program_header.file_size as usize, + page_offset: offset, } } } @@ -40,21 +51,28 @@ impl Pager for ElfSegmentPager { if offset >= self.pager_size { return_errno_with_message!(Errno::EINVAL, "offset exceeds pager size"); } + let vm_alloc_option = VmAllocOptions::new(1); let mut vm_frames = VmFrameVec::allocate(&vm_alloc_option)?; vm_frames.zero(); + let page_start = offset.align_down(PAGE_SIZE); let page_end = page_start + PAGE_SIZE; let page_range = page_start..page_end; - let data_range = self.offset..self.offset + self.segment_data.len(); - if is_intersected(&page_range, &data_range) { - let intersected_range = get_intersected_range(&page_range, &data_range); - let data_write_range = - (intersected_range.start - self.offset)..(intersected_range.end - self.offset); - let write_content = &self.segment_data[data_write_range]; + let segment_range = self.page_offset..self.page_offset + self.file_size; + if is_intersected(&page_range, &segment_range) { + let intersected_range = get_intersected_range(&page_range, &segment_range); + let segment_from_file_range = (intersected_range.start - self.page_offset) + ..(intersected_range.end - self.page_offset); + let mut segment_data = vec![0u8; segment_from_file_range.len()]; + self.file.seek(SeekFrom::Start( + self.file_offset + segment_from_file_range.start, + ))?; + self.file.read(&mut segment_data)?; let write_offset = intersected_range.start % PAGE_SIZE; - vm_frames.write_bytes(write_offset, write_content)?; + vm_frames.write_bytes(write_offset, &segment_data)?; } + let vm_frame = vm_frames.pop().unwrap(); Ok(vm_frame) } diff --git a/src/services/libs/jinux-std/src/process/elf/init_stack.rs b/src/services/libs/jinux-std/src/process/elf/init_stack.rs index cfdc043ae..ec98e46e8 100644 --- a/src/services/libs/jinux-std/src/process/elf/init_stack.rs +++ b/src/services/libs/jinux-std/src/process/elf/init_stack.rs @@ -16,7 +16,7 @@ use jinux_frame::{ }; use super::aux_vec::{AuxKey, AuxVec}; -use super::load_elf::ElfHeaderInfo; +use super::elf_file::Elf; pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000; pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB @@ -79,7 +79,6 @@ pub struct InitStack { impl InitStack { /// initialize user stack on base addr pub fn new( - // filename: CString, init_stack_top: Vaddr, init_stack_size: usize, argv: Vec, @@ -115,14 +114,9 @@ impl InitStack { self.init_stack_top - self.init_stack_size } - pub fn init( - &mut self, - root_vmar: &Vmar, - elf_header_info: &ElfHeaderInfo, - ph_addr: Vaddr, - ) -> Result<()> { + pub fn init(&mut self, root_vmar: &Vmar, elf: &Elf) -> Result<()> { self.map_and_zeroed(root_vmar)?; - self.write_stack_content(root_vmar, elf_header_info, ph_addr)?; + self.write_stack_content(root_vmar, elf)?; self.debug_print_stack_content(root_vmar); Ok(()) } @@ -161,12 +155,7 @@ impl InitStack { Ok(()) } - fn write_stack_content( - &mut self, - root_vmar: &Vmar, - elf_header_info: &ElfHeaderInfo, - ph_addr: Vaddr, - ) -> Result<()> { + fn write_stack_content(&mut self, root_vmar: &Vmar, elf: &Elf) -> Result<()> { // write envp string let envp_pointers = self.write_envp_strings(root_vmar)?; // write argv string @@ -176,11 +165,9 @@ impl InitStack { let random_value_pointer = self.write_bytes(&random_value, root_vmar)?; self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; - self.aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?; - self.aux_vec - .set(AuxKey::AT_PHNUM, elf_header_info.ph_num as u64)?; - self.aux_vec - .set(AuxKey::AT_PHENT, elf_header_info.ph_ent as u64)?; + self.aux_vec.set(AuxKey::AT_PHDR, elf.ph_addr()? as u64)?; + self.aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?; + self.aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?; self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers)?; self.write_aux_vec(root_vmar)?; self.write_envp_pointers(root_vmar, envp_pointers)?; diff --git a/src/services/libs/jinux-std/src/process/elf/load_elf.rs b/src/services/libs/jinux-std/src/process/elf/load_elf.rs index 3c4560214..b9c868983 100644 --- a/src/services/libs/jinux-std/src/process/elf/load_elf.rs +++ b/src/services/libs/jinux-std/src/process/elf/load_elf.rs @@ -1,6 +1,8 @@ //! This module is used to parse elf file content to get elf_load_info. //! When create a process from elf file, we will use the elf_load_info to construct the VmSpace +use crate::fs::file_handle::FileHandle; +use crate::process::elf::init_stack::InitStack; use crate::vm::perms::VmPerms; use crate::vm::vmo::VmoRightsOp; use crate::{ @@ -17,234 +19,102 @@ use xmas_elf::program::{self, ProgramHeader64}; use super::elf_file::Elf; use super::elf_segment_pager::ElfSegmentPager; -use super::init_stack::InitStack; + +/// load elf to the root vmar. this function will +/// 1. read the vaddr of each segment to get all elf pages. +/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar. +/// 3. write proper content to the init stack. +pub fn load_elf_to_root_vmar( + file_header: &[u8], + elf_file: Arc, + root_vmar: &Vmar, + argv: Vec, + envp: Vec, +) -> Result { + let elf = Elf::parse_elf(file_header)?; + map_segment_vmos(&elf, root_vmar, elf_file)?; + let mut init_stack = InitStack::new_default_config(argv, envp); + init_stack.init(root_vmar, &elf)?; + let elf_load_info = ElfLoadInfo::new(elf.entry_point(), init_stack.user_stack_top()); + debug!("load elf succeeds."); + Ok(elf_load_info) +} pub struct ElfLoadInfo { - segments: Vec, - init_stack: InitStack, - elf_header_info: ElfHeaderInfo, -} - -pub struct ElfSegment { - /// The virtual addr where to put the segment. - pub virtual_addr: Vaddr, - /// The segment's size in memory, in bytes. - pub mem_size: usize, - /// The segment's offset in origin elf file - pub offset: usize, - /// The size the segment has in origin elf file, in bytes - pub file_size: usize, - type_: program::Type, - vm_perm: VmPerm, -} - -#[derive(Debug, Clone, Copy, Default)] -/// Info parsed from elf header. The entry point is used to set rip -/// The other info is used to set auxv vectors. -pub struct ElfHeaderInfo { - /// the entry point of the elf - pub entry_point: Vaddr, - /// page header table offset - pub ph_off: u64, - /// number of program headers - pub ph_num: u16, - /// The size of a program header - pub ph_ent: u16, -} - -impl ElfSegment { - fn parse_elf_segment(program_header: ProgramHeader64) -> Result { - let start = program_header.virtual_addr as Vaddr; - let end = start + program_header.mem_size as Vaddr; - let type_ = program_header - .get_type() - .map_err(|_| Error::new(Errno::ENOEXEC))?; - let vm_perm = Self::parse_segment_perm(program_header.flags)?; - Ok(Self { - virtual_addr: program_header.virtual_addr as _, - mem_size: program_header.mem_size as usize, - offset: program_header.offset as usize, - file_size: program_header.file_size as usize, - type_, - vm_perm, - }) - } - - pub fn parse_segment_perm(flags: xmas_elf::program::Flags) -> Result { - if !flags.is_read() { - return_errno_with_message!(Errno::ENOEXEC, "unreadable segment"); - } - let mut vm_perm = VmPerm::R; - if flags.is_write() { - vm_perm |= VmPerm::W; - } - if flags.is_execute() { - vm_perm |= VmPerm::X; - } - Ok(vm_perm) - } - - fn contains_program_headers_table(&self, ph_offset: usize) -> bool { - // program headers table is at ph_offset of elf file - self.offset <= ph_offset && ph_offset < self.offset + self.file_size - } - - /// If this segment contains ph table, then returns the ph table addr - /// Otherwise, returns None - pub fn program_headers_table_addr(&self, ph_offset: usize) -> Option { - if self.contains_program_headers_table(ph_offset) { - Some(ph_offset - self.offset + self.virtual_addr) - } else { - None - } - } - - pub fn is_loadable(&self) -> bool { - self.type_ == program::Type::Load - } - - pub fn start_address(&self) -> Vaddr { - self.virtual_addr - } - - pub fn end_address(&self) -> Vaddr { - self.virtual_addr + self.mem_size - } - - pub fn init_segment_vmo(&self, elf_file_content: &'static [u8]) -> Vmo { - let vmo_start = self.start_address().align_down(PAGE_SIZE); - let vmo_end = self.end_address().align_up(PAGE_SIZE); - let segment_len = vmo_end - vmo_start; - let pager = Arc::new(ElfSegmentPager::new(elf_file_content, self)) as Arc; - let vmo_alloc_options: VmoOptions = VmoOptions::new(segment_len).pager(pager); - vmo_alloc_options.alloc().unwrap() - } - - // create vmo for each segment and map the segment to root_vmar - fn map_segment_vmo( - &self, - root_vmar: &Vmar, - elf_file_content: &'static [u8], - ) -> Result<()> { - let vmo = self.init_segment_vmo(elf_file_content).to_dyn(); - let perms = VmPerms::from(self.vm_perm); - // The segment may not be aligned to page - let offset = self.start_address().align_down(PAGE_SIZE); - let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset); - let map_addr = vm_map_options.build()?; - Ok(()) - } + entry_point: Vaddr, + user_stack_top: Vaddr, } impl ElfLoadInfo { - fn with_capacity( - capacity: usize, - init_stack: InitStack, - elf_header_info: ElfHeaderInfo, - ) -> Self { + pub fn new(entry_point: Vaddr, user_stack_top: Vaddr) -> Self { Self { - segments: Vec::with_capacity(capacity), - init_stack, - elf_header_info, - } - } - - fn add_segment(&mut self, elf_segment: ElfSegment) { - self.segments.push(elf_segment); - } - - pub fn parse_elf_data( - elf_file_content: &'static [u8], - argv: Vec, - envp: Vec, - ) -> Result { - let elf_file = Elf::parse_elf(elf_file_content)?; - // parse elf header - let elf_header_info = ElfHeaderInfo::parse_elf_header(&elf_file); - // FIXME: only contains load segment? - let ph_count = elf_file.program_headers.len(); - let init_stack = InitStack::new_default_config(argv, envp); - let mut elf_load_info = ElfLoadInfo::with_capacity(ph_count, init_stack, elf_header_info); - - // parse each segemnt - for program_header in elf_file.program_headers { - let elf_segment = ElfSegment::parse_elf_segment(program_header)?; - if elf_segment.is_loadable() { - elf_load_info.add_segment(elf_segment) - } - } - - Ok(elf_load_info) - } - - /// init vmo for each segment and then map segment to root vmar - pub fn map_segment_vmos( - &self, - root_vmar: &Vmar, - elf_file_content: &'static [u8], - ) -> Result<()> { - for segment in &self.segments { - segment.map_segment_vmo(root_vmar, elf_file_content)?; - } - Ok(()) - } - - pub fn init_stack(&mut self, root_vmar: &Vmar, file_content: &[u8]) -> Result<()> { - let ph_addr = self.program_headers_table_addr()?; - self.init_stack - .init(root_vmar, &self.elf_header_info, ph_addr)?; - Ok(()) - } - - fn program_headers_table_addr(&self) -> Result { - let ph_offset = self.elf_header_info.ph_off as usize; - for segment in &self.segments { - if let Some(ph_addr) = segment.program_headers_table_addr(ph_offset) { - return Ok(ph_addr); - } - } - return_errno_with_message!( - Errno::ENOEXEC, - "can not find program header table address in elf" - ); - } - - pub fn entry_point(&self) -> u64 { - self.elf_header_info.entry_point as u64 - } - - pub fn user_stack_top(&self) -> u64 { - self.init_stack.user_stack_top() as u64 - } - - pub fn argc(&self) -> u64 { - self.init_stack.argc() - } - - pub fn argv(&self) -> u64 { - self.init_stack.argv() - } - - pub fn envc(&self) -> u64 { - self.init_stack.envc() - } - - pub fn envp(&self) -> u64 { - self.init_stack.envp() - } -} - -impl ElfHeaderInfo { - fn parse_elf_header(elf_file: &Elf) -> Self { - let entry_point = elf_file.elf_header.pt2.entry_point as Vaddr; - let ph_off = elf_file.elf_header.pt2.ph_offset; - let ph_num = elf_file.elf_header.pt2.ph_count; - let ph_ent = elf_file.elf_header.pt2.ph_entry_size; - ElfHeaderInfo { entry_point, - ph_off, - ph_num, - ph_ent, + user_stack_top, } } + + pub fn entry_point(&self) -> Vaddr { + self.entry_point + } + + pub fn user_stack_top(&self) -> Vaddr { + self.user_stack_top + } +} + +/// init vmo for each segment and then map segment to root vmar +pub fn map_segment_vmos( + elf: &Elf, + root_vmar: &Vmar, + elf_file: Arc, +) -> Result<()> { + for program_header in &elf.program_headers { + let type_ = program_header + .get_type() + .map_err(|_| Error::new(Errno::ENOEXEC))?; + if type_ == program::Type::Load { + let vmo = init_segment_vmo(program_header, elf_file.clone())?; + map_segment_vmo(program_header, vmo, root_vmar, elf_file.clone())?; + } + } + Ok(()) +} + +/// map the segment vmo to root_vmar +fn map_segment_vmo( + program_header: &ProgramHeader64, + vmo: Vmo, + root_vmar: &Vmar, + elf_file: Arc, +) -> Result<()> { + let perms = VmPerms::from(parse_segment_perm(program_header.flags)?); + let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE); + let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset); + let map_addr = vm_map_options.build()?; + Ok(()) +} + +/// create vmo for each segment +fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: Arc) -> Result { + let vmo_start = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE); + let vmo_end = (program_header.virtual_addr as Vaddr + program_header.mem_size as Vaddr) + .align_up(PAGE_SIZE); + let segment_len = vmo_end - vmo_start; + let pager = Arc::new(ElfSegmentPager::new(elf_file, &program_header)) as Arc; + let vmo_alloc_options: VmoOptions = VmoOptions::new(segment_len).pager(pager); + Ok(vmo_alloc_options.alloc()?.to_dyn()) +} + +fn parse_segment_perm(flags: xmas_elf::program::Flags) -> Result { + if !flags.is_read() { + return_errno_with_message!(Errno::ENOEXEC, "unreadable segment"); + } + let mut vm_perm = VmPerm::R; + if flags.is_write() { + vm_perm |= VmPerm::W; + } + if flags.is_execute() { + vm_perm |= VmPerm::X; + } + Ok(vm_perm) } diff --git a/src/services/libs/jinux-std/src/process/elf/mod.rs b/src/services/libs/jinux-std/src/process/elf/mod.rs index 5dc96c62f..aaf33e662 100644 --- a/src/services/libs/jinux-std/src/process/elf/mod.rs +++ b/src/services/libs/jinux-std/src/process/elf/mod.rs @@ -1,26 +1,8 @@ -pub mod aux_vec; -pub mod elf_file; -pub mod elf_segment_pager; -pub mod init_stack; -pub mod load_elf; +mod aux_vec; +mod elf_file; +mod elf_segment_pager; +mod init_stack; +mod load_elf; -use self::load_elf::ElfLoadInfo; -use crate::{prelude::*, rights::Full, vm::vmar::Vmar}; - -/// load elf to the root vmar. this function will -/// 1. read the vaddr of each segment to get all elf pages. -/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar. -/// 3. write proper content to the init stack. -pub fn load_elf_to_root_vmar( - elf_file_content: &'static [u8], - root_vmar: &Vmar, - argv: Vec, - envp: Vec, -) -> Result { - let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, argv, envp)?; - elf_load_info.map_segment_vmos(root_vmar, elf_file_content)?; - elf_load_info.init_stack(root_vmar, elf_file_content)?; - debug!("load elf succeeds."); - - Ok(elf_load_info) -} +pub use init_stack::INIT_STACK_SIZE; +pub use load_elf::{load_elf_to_root_vmar, ElfLoadInfo}; diff --git a/src/services/libs/jinux-std/src/process/mod.rs b/src/services/libs/jinux-std/src/process/mod.rs index dcf1638f4..6374b07f9 100644 --- a/src/services/libs/jinux-std/src/process/mod.rs +++ b/src/services/libs/jinux-std/src/process/mod.rs @@ -1,5 +1,6 @@ use core::sync::atomic::{AtomicI32, Ordering}; +use self::elf::{ElfLoadInfo, load_elf_to_root_vmar}; use self::posix_thread::posix_thread_ext::PosixThreadExt; use self::process_group::ProcessGroup; use self::process_vm::user_heap::UserHeap; @@ -10,16 +11,18 @@ use self::signal::sig_disposition::SigDispositions; use self::signal::sig_queues::SigQueues; use self::signal::signals::kernel::KernelSignal; use self::status::ProcessStatus; +use crate::fs::file_handle::FileHandle; use crate::fs::file_table::FileTable; +use crate::fs::fs_resolver::AT_FDCWD; use crate::fs::fs_resolver::{FsPath, FsResolver}; -use crate::fs::utils::AccessMode; +use crate::fs::utils::{AccessMode, SeekFrom}; use crate::prelude::*; use crate::rights::Full; use crate::thread::{thread_table, Thread}; use crate::tty::get_n_tty; use crate::vm::vmar::Vmar; -use alloc::string::String; use jinux_frame::sync::WaitQueue; +use jinux_frame::task::Task; pub mod clone; pub mod elf; @@ -45,7 +48,7 @@ const INIT_PROCESS_PID: Pid = 1; pub struct Process { // Immutable Part pid: Pid, - elf_path: Option, + executable_path: Option, user_vm: Option, root_vmar: Arc>, /// wait for child status changed @@ -96,7 +99,7 @@ impl Process { pid: Pid, parent: Weak, threads: Vec>, - elf_path: Option, + executable_path: Option, user_vm: Option, root_vmar: Arc>, process_group: Weak, @@ -111,7 +114,7 @@ impl Process { Self { pid, threads: Mutex::new(threads), - elf_path, + executable_path, user_vm, root_vmar, waiting_children, @@ -139,12 +142,11 @@ impl Process { /// init a user process and run the process pub fn spawn_user_process( - filename: CString, - elf_file_content: &'static [u8], + filename: String, argv: Vec, envp: Vec, ) -> Arc { - let process = Process::create_user_process(filename, elf_file_content, argv, envp); + let process = Process::create_user_process(filename, argv, envp); // FIXME: How to determine the fg process group? let pgid = process.pgid(); // FIXME: tty should be a parameter? @@ -155,24 +157,19 @@ impl Process { } fn create_user_process( - elf_path: CString, - elf_file_content: &'static [u8], + executable_path: String, argv: Vec, envp: Vec, ) -> Arc { let user_process = Arc::new_cyclic(|weak_process_ref| { let weak_process = weak_process_ref.clone(); - let cloned_filename = Some(elf_path.clone()); + let cloned_filename = Some(executable_path.clone()); let root_vmar = Vmar::::new_root().unwrap(); -<<<<<<< HEAD - let thread = Thread::new_posix_thread_from_elf( -======= let fs = FsResolver::new(); let thread = Thread::new_posix_thread_from_executable( ->>>>>>> 0255134... fix &root_vmar, - elf_path, - elf_file_content, + &fs, + executable_path, weak_process, argv, envp, @@ -362,8 +359,8 @@ impl Process { self.children.lock().len() != 0 } - pub fn filename(&self) -> Option<&CString> { - self.elf_path.as_ref() + pub fn executable_path(&self) -> Option<&String> { + self.executable_path.as_ref() } pub fn status(&self) -> &Mutex { @@ -387,77 +384,3 @@ impl Process { pub fn get_init_process() -> Option> { process_table::pid_to_process(INIT_PROCESS_PID) } - -/// Set up root vmar for an executable. -/// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables. -/// If the interpreter program(the program behind !#) of shebang executable is also a shebang, -/// then it will trigger recursion. We will try to setup root vmar for the interpreter program. -/// I guess for most cases, setting the recursion_limit as 1 should be enough. -/// because the interpreter game is usually an elf binary(e.g., /bin/bash) -pub fn setup_root_vmar( - executable_path: String, - argv: Vec, - envp: Vec, - fs_resolver: &FsResolver, - root_vmar: Vmar, - recursion_limit: usize, -) -> Result<()> { - use crate::fs::fs_resolver::AT_FDCWD; - let fs_path = FsPath::new(AT_FDCWD, &executable_path)?; - let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?; - // read the first page of file header - let mut file_header_buffer = [0u8; PAGE_SIZE]; - file.read(&mut file_header_buffer)?; - if recursion_limit > 0 - && file_header_buffer.starts_with(b"!#") - && file_header_buffer.contains(&b'\n') - { - return set_up_root_vmar_for_shebang( - argv, - envp, - &file_header_buffer, - fs_resolver, - root_vmar, - recursion_limit, - ); - } - - todo!() -} - -fn set_up_root_vmar_for_shebang( - argv: Vec, - envp: Vec, - file_header_buffer: &[u8], - fs_resolver: &FsResolver, - root_vmar: Vmar, - recursion_limit: usize, -) -> Result<()> { - let first_line_len = file_header_buffer.iter().position(|&c| c == b'\n').unwrap(); - // skip !# - let shebang_header = &file_header_buffer[2..first_line_len]; - let mut shebang_argv = Vec::new(); - for arg in shebang_header.split(|&c| c == b' ') { - let arg = CString::new(arg)?; - shebang_argv.push(arg); - } - if shebang_argv.len() != 1 { - return_errno_with_message!( - Errno::EINVAL, - "One and only one intpreter program should be specified" - ); - } - for origin_arg in argv.into_iter() { - shebang_argv.push(origin_arg); - } - use alloc::string::ToString; - let shebang_path = shebang_argv[0].to_str()?.to_string(); - setup_root_vmar( - shebang_path, - shebang_argv, - envp, - fs_resolver, - root_vmar, - recursion_limit - 1, - ) -} diff --git a/src/services/libs/jinux-std/src/process/posix_thread/name.rs b/src/services/libs/jinux-std/src/process/posix_thread/name.rs index b24823dcc..09c1b260c 100644 --- a/src/services/libs/jinux-std/src/process/posix_thread/name.rs +++ b/src/services/libs/jinux-std/src/process/posix_thread/name.rs @@ -14,10 +14,9 @@ impl ThreadName { } } - pub fn new_from_elf_path(elf_path: &CStr) -> Result { + pub fn new_from_executable_path(elf_path: &str) -> Result { let mut thread_name = ThreadName::new(); let elf_file_name = elf_path - .to_str()? .split('/') .last() .ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?; diff --git a/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs index 574d158f8..2802e4457 100644 --- a/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs +++ b/src/services/libs/jinux-std/src/process/posix_thread/posix_thread_ext.rs @@ -1,8 +1,10 @@ +use alloc::string::String; use jinux_frame::{cpu::CpuContext, user::UserSpace}; use crate::{ + fs::fs_resolver::FsResolver, prelude::*, - process::{elf::load_elf_to_root_vmar, Process}, + process::{setup_root_vmar, Process}, rights::Full, thread::{allocate_tid, Thread}, vm::vmar::Vmar, @@ -13,8 +15,8 @@ pub trait PosixThreadExt { fn as_posix_thread(&self) -> Option<&PosixThread>; fn new_posix_thread_from_executable( root_vmar: &Vmar, - elf_path: CString, - elf_file_content: &'static [u8], + fs_resolver: &FsResolver, + executable_path: String, process: Weak, argv: Vec, envp: Vec, @@ -25,20 +27,28 @@ impl PosixThreadExt for Thread { /// This function should only be called when launch shell() fn new_posix_thread_from_executable( root_vmar: &Vmar, - elf_path: CString, - elf_file_content: &'static [u8], + fs_resolver: &FsResolver, + executable_path: String, process: Weak, argv: Vec, envp: Vec, ) -> Arc { - let elf_load_info = load_elf_to_root_vmar(elf_file_content, &root_vmar, argv, envp) - .expect("Load Elf failed"); + let elf_load_info = setup_root_vmar( + executable_path.clone(), + argv, + envp, + fs_resolver, + root_vmar, + 1, + ) + .unwrap(); + let vm_space = root_vmar.vm_space().clone(); let mut cpu_ctx = CpuContext::default(); - cpu_ctx.set_rip(elf_load_info.entry_point()); - cpu_ctx.set_rsp(elf_load_info.user_stack_top()); + cpu_ctx.set_rip(elf_load_info.entry_point() as _); + cpu_ctx.set_rsp(elf_load_info.user_stack_top() as _); let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); - let thread_name = Some(ThreadName::new_from_elf_path(&elf_path).unwrap()); + let thread_name = Some(ThreadName::new_from_executable_path(&executable_path).unwrap()); let tid = allocate_tid(); let thread_builder = PosixThreadBuilder::new(tid, user_space) .thread_name(thread_name) diff --git a/src/services/libs/jinux-std/src/process/rlimit.rs b/src/services/libs/jinux-std/src/process/rlimit.rs index 49a392c8a..73160acb7 100644 --- a/src/services/libs/jinux-std/src/process/rlimit.rs +++ b/src/services/libs/jinux-std/src/process/rlimit.rs @@ -4,7 +4,7 @@ use crate::prelude::*; -use super::{elf::init_stack::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT}; +use super::{elf::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT}; pub struct ResourceLimits { rlimits: [RLimit64; RLIMIT_COUNT], diff --git a/src/services/libs/jinux-std/src/process/signal/mod.rs b/src/services/libs/jinux-std/src/process/signal/mod.rs index 3f6f4d391..03d1c53ec 100644 --- a/src/services/libs/jinux-std/src/process/signal/mod.rs +++ b/src/services/libs/jinux-std/src/process/signal/mod.rs @@ -31,7 +31,7 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> { let current_thread = current_thread!(); let posix_thread = current_thread.as_posix_thread().unwrap(); let pid = current.pid(); - let process_name = current.filename().unwrap(); + let process_name = current.executable_path().unwrap(); let sig_mask = posix_thread.sig_mask().lock().clone(); let mut thread_sig_queues = posix_thread.sig_queues().lock(); let mut proc_sig_queues = current.sig_queues().lock(); diff --git a/src/services/libs/jinux-std/src/syscall/execve.rs b/src/services/libs/jinux-std/src/syscall/execve.rs index f4898fc17..96de5924e 100644 --- a/src/services/libs/jinux-std/src/syscall/execve.rs +++ b/src/services/libs/jinux-std/src/syscall/execve.rs @@ -2,9 +2,9 @@ use jinux_frame::cpu::CpuContext; use super::{constants::*, SyscallReturn}; use crate::log_syscall_entry; -use crate::process::elf::load_elf_to_root_vmar; use crate::process::posix_thread::name::ThreadName; use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; +use crate::process::setup_root_vmar; use crate::util::{read_cstring_from_user, read_val_from_user}; use crate::{prelude::*, syscall::SYS_EXECVE}; @@ -15,24 +15,22 @@ pub fn sys_execve( context: &mut CpuContext, ) -> Result { log_syscall_entry!(SYS_EXECVE); - let elf_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; + let executable_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; + let executable_path = executable_path.into_string().unwrap(); let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?; let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?; debug!( "filename: {:?}, argv = {:?}, envp = {:?}", - elf_path, argv, envp + executable_path, argv, envp ); - if elf_path != CString::new("./hello").unwrap() { - panic!("Unknown filename."); - } // FIXME: should we set thread name in execve? let current_thread = current_thread!(); let posix_thread = current_thread.as_posix_thread().unwrap(); let mut thread_name = posix_thread.thread_name().lock(); - let new_thread_name = ThreadName::new_from_elf_path(&elf_path)?; + let new_thread_name = ThreadName::new_from_executable_path(&executable_path)?; *thread_name = Some(new_thread_name); - let elf_file_content = crate::user_apps::read_execve_hello_content(); + // let elf_file_content = crate::user_apps::read_execve_hello_content(); let current = current!(); // destroy root vmars let root_vmar = current.root_vmar(); @@ -42,8 +40,8 @@ pub fn sys_execve( .expect("[Internal Error] User process should have user vm"); user_vm.set_default(); // load elf content to new vm space - let elf_load_info = - load_elf_to_root_vmar(elf_file_content, root_vmar, argv, envp).expect("load elf failed"); + let fs_resolver = &*current.fs().read(); + let elf_load_info = setup_root_vmar(executable_path, argv, envp, fs_resolver, root_vmar, 1)?; debug!("load elf in execve succeeds"); // set signal disposition to default current.sig_dispositions().lock().inherit(); @@ -53,10 +51,10 @@ pub fn sys_execve( context.fs_base = defalut_content.fs_base; context.fp_regs = defalut_content.fp_regs; // set new entry point - context.gp_regs.rip = elf_load_info.entry_point(); + context.gp_regs.rip = elf_load_info.entry_point() as _; debug!("entry_point: 0x{:x}", elf_load_info.entry_point()); // set new user stack top - context.gp_regs.rsp = elf_load_info.user_stack_top(); + context.gp_regs.rsp = elf_load_info.user_stack_top() as _; debug!("user stack top: 0x{:x}", elf_load_info.user_stack_top()); Ok(SyscallReturn::NoReturn) } diff --git a/src/services/libs/jinux-std/src/syscall/readlink.rs b/src/services/libs/jinux-std/src/syscall/readlink.rs index f0bb514d6..bad28e9c1 100644 --- a/src/services/libs/jinux-std/src/syscall/readlink.rs +++ b/src/services/libs/jinux-std/src/syscall/readlink.rs @@ -26,7 +26,7 @@ pub fn sys_readlinkat( let current = current!(); if pathname == CString::new("/proc/self/exe")? { // "proc/self/exe" is used to read the filename of current executable - let process_file_name = current.filename().unwrap(); + let process_file_name = current.executable_path().unwrap(); debug!("process exec filename= {:?}", process_file_name); // readlink does not append a terminating null byte to buf let bytes = process_file_name.as_bytes(); diff --git a/src/services/libs/jinux-std/src/user_apps.rs b/src/services/libs/jinux-std/src/user_apps.rs index bd17b5eee..88d654848 100644 --- a/src/services/libs/jinux-std/src/user_apps.rs +++ b/src/services/libs/jinux-std/src/user_apps.rs @@ -1,44 +1,21 @@ -use crate::fs::{ - fs_resolver::{FsPath, FsResolver}, - utils::AccessMode, -}; use crate::prelude::*; pub struct UserApp { - pub elf_path: CString, - pub app_content: Vec, + pub executable_path: String, pub argv: Vec, pub envp: Vec, } impl UserApp { - pub fn new(elf_path: &str) -> Result { - let app_name = CString::new(elf_path).unwrap(); - let app_content = { - let fs = FsResolver::new(); - let file = fs.open(&FsPath::try_from(elf_path)?, AccessMode::O_RDONLY as u32, 0)?; - let mut content = Vec::new(); - let len = file.read_to_end(&mut content)?; - if len != file.len() { - return_errno_with_message!(Errno::EINVAL, "read len is not equal to file size"); - } - content - }; + pub fn new(executable_path: &str) -> Result { + let app_name = String::from(executable_path); + let arg0 = CString::new(executable_path)?; Ok(UserApp { - elf_path: app_name, - app_content, - argv: Vec::new(), + executable_path: app_name, + argv: vec![arg0], envp: Vec::new(), }) } - - pub fn set_argv(&mut self, argv: Vec) { - self.argv = argv; - } - - pub fn set_envp(&mut self, envp: Vec) { - self.envp = envp; - } } pub fn get_all_apps() -> Result> { @@ -81,7 +58,7 @@ pub fn get_busybox_app() -> Result { // busybox let mut busybox = UserApp::new("/busybox/busybox")?; // -l option means the busybox is running as logging shell - let argv = ["/busybox", "sh", "-l"]; + let argv = ["sh", "-l"]; let envp = [ "SHELL=/bin/sh", "PWD=/", @@ -92,21 +69,13 @@ pub fn get_busybox_app() -> Result { "OLDPWD=/", ]; - let argv = to_vec_cstring(&argv)?; - let envp = to_vec_cstring(&envp)?; - busybox.set_argv(argv); - busybox.set_envp(envp); + let mut argv = to_vec_cstring(&argv)?; + let mut envp = to_vec_cstring(&envp)?; + busybox.argv.append(&mut argv); + busybox.envp.append(&mut envp); Ok(busybox) } -fn read_execve_content() -> &'static [u8] { - include_bytes!("../../../../apps/execve/execve") -} - -pub fn read_execve_hello_content() -> &'static [u8] { - include_bytes!("../../../../apps/execve/hello") -} - fn to_vec_cstring(raw_strs: &[&str]) -> Result> { let mut res = Vec::new(); for raw_str in raw_strs {