diff --git a/src/apps/bin/Makefile b/src/apps/bin/Makefile new file mode 100644 index 00000000..058792aa --- /dev/null +++ b/src/apps/bin/Makefile @@ -0,0 +1,4 @@ +.PHONY: all + +all: + ln -sf /busybox/busybox sh \ No newline at end of file diff --git a/src/apps/bin/sh b/src/apps/bin/sh new file mode 120000 index 00000000..032fe1be --- /dev/null +++ b/src/apps/bin/sh @@ -0,0 +1 @@ +/busybox/busybox \ No newline at end of file diff --git a/src/apps/scripts/run_tests.sh b/src/apps/scripts/run_tests.sh new file mode 100755 index 00000000..ca45bbfd --- /dev/null +++ b/src/apps/scripts/run_tests.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +set -e + +echo "Running tests......" +tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test" + +for testcase in ${tests} +do + echo "Running test ${testcase}......" + ${testcase} +done +echo "All tests passed" \ No newline at end of file diff --git a/src/services/libs/jinux-std/src/lib.rs b/src/services/libs/jinux-std/src/lib.rs index daf62222..b37bdb0e 100644 --- a/src/services/libs/jinux-std/src/lib.rs +++ b/src/services/libs/jinux-std/src/lib.rs @@ -21,7 +21,7 @@ use crate::{ prelude::*, thread::{kernel_thread::KernelThreadExt, Thread}, - user_apps::{get_all_apps, get_busybox_app, UserApp}, + user_apps::{get_busybox_app, UserApp}, }; use process::Process; @@ -68,21 +68,6 @@ pub fn init_thread() { 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 { executable_path: app_name, 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 eee66533..d3892f36 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 @@ -51,7 +51,7 @@ impl Elf { pub fn entry_point(&self) -> Vaddr { self.elf_header.pt2.entry_point as Vaddr } - /// page header table offset + /// program header table offset pub fn ph_off(&self) -> u64 { self.elf_header.pt2.ph_offset } 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 17317c79..4d332ea7 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,5 +1,3 @@ -use core::ops::Range; - use crate::fs::file_handle::FileHandle; use crate::fs::utils::SeekFrom; use crate::prelude::*; 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 ec98e46e..628ce1f6 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 @@ -156,6 +156,14 @@ impl InitStack { } fn write_stack_content(&mut self, root_vmar: &Vmar, elf: &Elf) -> Result<()> { + // write a zero page. When a user program tries to read a cstring(like argv) from init stack, + // it will typically read 4096 bytes and then find the first '\0' in the buffer + // (we now read 128 bytes, which is set by MAX_FILENAME_LEN). + // If we don't have this zero page, the read may go into guard page, + // which will cause unrecoverable page fault(The guard page is not backed up by any vmo). + // So we add a zero page here, to ensure the read will not go into guard page. + // FIXME: Some other OSes put the first page of excutable file here. + self.write_bytes(&[0u8; PAGE_SIZE], root_vmar)?; // write envp string let envp_pointers = self.write_envp_strings(root_vmar)?; // write argv string @@ -201,6 +209,7 @@ impl InitStack { let mut argv_pointers = Vec::with_capacity(argv.len()); for argv in argv.iter().rev() { let pointer = self.write_cstring(argv, root_vmar)?; + debug!("argv address = 0x{:x}", pointer); argv_pointers.push(pointer); } argv_pointers.reverse(); diff --git a/src/services/libs/jinux-std/src/process/mod.rs b/src/services/libs/jinux-std/src/process/mod.rs index 6374b07f..40fae8c1 100644 --- a/src/services/libs/jinux-std/src/process/mod.rs +++ b/src/services/libs/jinux-std/src/process/mod.rs @@ -1,6 +1,6 @@ use core::sync::atomic::{AtomicI32, Ordering}; -use self::elf::{ElfLoadInfo, load_elf_to_root_vmar}; +use self::elf::{load_elf_to_root_vmar, ElfLoadInfo}; use self::posix_thread::posix_thread_ext::PosixThreadExt; use self::process_group::ProcessGroup; use self::process_vm::user_heap::UserHeap; @@ -22,7 +22,6 @@ use crate::thread::{thread_table, Thread}; use crate::tty::get_n_tty; use crate::vm::vmar::Vmar; use jinux_frame::sync::WaitQueue; -use jinux_frame::task::Task; pub mod clone; pub mod elf; @@ -384,3 +383,76 @@ 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 { + 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.seek(SeekFrom::Start(0))?; + 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, + ); + } + let elf_file = Arc::new(FileHandle::new_inode_handle(file)); + load_elf_to_root_vmar(&file_header_buffer, elf_file, &root_vmar, argv, envp) +} + +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); + } + 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/mod.rs b/src/services/libs/jinux-std/src/process/posix_thread/mod.rs index b1c96c72..c9d31d89 100644 --- a/src/services/libs/jinux-std/src/process/posix_thread/mod.rs +++ b/src/services/libs/jinux-std/src/process/posix_thread/mod.rs @@ -117,11 +117,16 @@ impl PosixThread { // If clear_ctid !=0 ,do a futex wake and write zero to the clear_ctid addr. debug!("wake up ctid"); if *clear_ctid != 0 { + debug!("futex wake"); futex_wake(*clear_ctid, 1)?; + debug!("write ctid"); // FIXME: the correct write length? - write_val_to_user(*clear_ctid, &0i32)?; + debug!("ctid = 0x{:x}", *clear_ctid); + write_val_to_user(*clear_ctid, &0u32).unwrap(); + debug!("clear ctid"); *clear_ctid = 0; } + debug!("wake up ctid succeeds"); // exit the robust list: walk the robust list; mark futex words as dead and do futex wake self.wake_robust_list(tid); 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 09c1b260..22920c98 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 @@ -1,6 +1,8 @@ use crate::prelude::*; pub const MAX_THREAD_NAME_LEN: usize = 16; + +#[derive(Debug)] pub struct ThreadName { inner: [u8; MAX_THREAD_NAME_LEN], count: usize, @@ -14,13 +16,13 @@ impl ThreadName { } } - pub fn new_from_executable_path(elf_path: &str) -> Result { + pub fn new_from_executable_path(executable_path: &str) -> Result { let mut thread_name = ThreadName::new(); - let elf_file_name = elf_path + let executable_file_name = executable_path .split('/') .last() .ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?; - let name = CString::new(elf_file_name)?; + let name = CString::new(executable_file_name)?; thread_name.set_name(&name)?; Ok(thread_name) } @@ -40,7 +42,7 @@ impl ThreadName { Ok(()) } - pub fn get_name(&self) -> Result> { - Ok(Some(&(CStr::from_bytes_with_nul(&self.inner)?))) + pub fn name(&self) -> Result> { + Ok(Some(&(CStr::from_bytes_until_nul(&self.inner)?))) } } diff --git a/src/services/libs/jinux-std/src/syscall/execve.rs b/src/services/libs/jinux-std/src/syscall/execve.rs index 96de5924..4ee07b3d 100644 --- a/src/services/libs/jinux-std/src/syscall/execve.rs +++ b/src/services/libs/jinux-std/src/syscall/execve.rs @@ -29,6 +29,9 @@ pub fn sys_execve( let mut thread_name = posix_thread.thread_name().lock(); let new_thread_name = ThreadName::new_from_executable_path(&executable_path)?; *thread_name = Some(new_thread_name); + // clear ctid + // FIXME: should we clear ctid when execve? + *posix_thread.clear_child_tid().lock() = 0; // let elf_file_content = crate::user_apps::read_execve_hello_content(); let current = current!(); diff --git a/src/services/libs/jinux-std/src/syscall/prctl.rs b/src/services/libs/jinux-std/src/syscall/prctl.rs index 2ac1dae2..9b3c0761 100644 --- a/src/services/libs/jinux-std/src/syscall/prctl.rs +++ b/src/services/libs/jinux-std/src/syscall/prctl.rs @@ -10,14 +10,14 @@ use super::SYS_PRCTL; pub fn sys_prctl(option: i32, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> Result { log_syscall_entry!(SYS_PRCTL); let prctl_cmd = PrctlCmd::from_args(option, arg2, arg3, arg4, arg5)?; - debug!("prctl cmd = {:?}", prctl_cmd); + debug!("prctl cmd = {:x?}", prctl_cmd); let current_thread = current_thread!(); let posix_thread = current_thread.as_posix_thread().unwrap(); match prctl_cmd { PrctlCmd::PR_GET_NAME(write_to_addr) => { let thread_name = posix_thread.thread_name().lock(); if let Some(thread_name) = &*thread_name { - if let Some(thread_name) = thread_name.get_name()? { + if let Some(thread_name) = thread_name.name()? { write_bytes_to_user(write_to_addr, thread_name.to_bytes_with_nul())?; } } diff --git a/src/services/libs/jinux-std/src/thread/kernel_thread.rs b/src/services/libs/jinux-std/src/thread/kernel_thread.rs index 70b4ea93..378768b7 100644 --- a/src/services/libs/jinux-std/src/thread/kernel_thread.rs +++ b/src/services/libs/jinux-std/src/thread/kernel_thread.rs @@ -23,7 +23,7 @@ pub trait KernelThreadExt { thread.run(); thread } - /// join a kernel thread + /// join a kernel thread, returns if the kernel thread exit fn join(&self); } @@ -36,7 +36,6 @@ impl KernelThreadExt for Thread { where F: Fn() + Send + Sync + 'static, { - let thread_fn = move || { task_fn(); let current_thread = current_thread!(); diff --git a/src/services/libs/jinux-std/src/user_apps.rs b/src/services/libs/jinux-std/src/user_apps.rs index 88d65484..baf1b6c1 100644 --- a/src/services/libs/jinux-std/src/user_apps.rs +++ b/src/services/libs/jinux-std/src/user_apps.rs @@ -18,42 +18,6 @@ impl UserApp { } } -pub fn get_all_apps() -> Result> { - let mut res = Vec::with_capacity(16); - - // Most simple hello world, written in assembly - let asm_hello_world = UserApp::new("hello_world/hello_world")?; - res.push(asm_hello_world); - - // Hello world, written in C language. - // Since glibc requires the elf path starts with "/", and we don't have filesystem now. - // So we manually add a leading "/" for app written in C language. - let hello_c = UserApp::new("/hello_c/hello")?; - res.push(hello_c); - - // Fork process, written in assembly - let asm_fork = UserApp::new("fork/fork")?; - res.push(asm_fork); - - // Execve, written in C language. - let execve_c = UserApp::new("/execve/execve")?; - res.push(execve_c); - - // Fork new process, written in C language. (Fork in glibc uses syscall clone actually) - let fork_c = UserApp::new("/fork_c/fork")?; - res.push(fork_c); - - // signal test - let signal_test = UserApp::new("/signal_c/signal_test")?; - res.push(signal_test); - - // pthread test - let pthread_test = UserApp::new("/pthread/pthread_test")?; - res.push(pthread_test); - - Ok(res) -} - pub fn get_busybox_app() -> Result { // busybox let mut busybox = UserApp::new("/busybox/busybox")?; @@ -65,7 +29,7 @@ pub fn get_busybox_app() -> Result { "LOGNAME=root", "HOME=/", "USER=root", - "PATH=", + "PATH=/bin", "OLDPWD=/", ];