refactor program loader

This commit is contained in:
Jianfeng Jiang 2023-03-14 15:35:38 +08:00 committed by Tate, Hongliang Tian
parent 63800a4f65
commit 330e495560
15 changed files with 148 additions and 187 deletions

43
src/Cargo.lock generated
View File

@ -62,11 +62,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "buddy_system_allocator"
version = "0.6.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4e85e760e105b46ae0bd1236578793c6c147ae7463fe95c8350296b8bfcb830"
checksum = "43f9365b6b0c9e1663ca4ca9440c00eda46bc85a3407070be8b5e0d8d1f29629"
dependencies = [
"spin 0.7.1",
"spin 0.9.4",
]
[[package]]
@ -202,6 +202,7 @@ dependencies = [
"jinux-frame",
"jinux-std",
"limine",
"x86_64",
]
[[package]]
@ -239,10 +240,10 @@ dependencies = [
"intrusive-collections",
"lazy_static",
"limine",
"linked_list_allocator",
"log",
"pod",
"spin 0.9.4",
"trapframe",
"uart_16550",
"volatile",
"x86",
@ -368,15 +369,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "linked_list_allocator"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "549ce1740e46b291953c4340adcd74c59bcf4308f4cac050fd33ba91b7168f4a"
dependencies = [
"spinning_top",
]
[[package]]
name = "lock_api"
version = "0.4.9"
@ -527,12 +519,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13287b4da9d1207a4f4929ac390916d64eacfe236a487e9a9f5b3be392be5162"
[[package]]
name = "spin"
version = "0.9.4"
@ -542,15 +528,6 @@ dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c"
dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "1.0.109"
@ -616,6 +593,16 @@ dependencies = [
"winnow",
]
[[package]]
name = "trapframe"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f287ee70169f5bfddba441baf901b620e3655f16fa7815f48a7e100ec6d86a8f"
dependencies = [
"raw-cpuid",
"x86_64",
]
[[package]]
name = "typeflags"
version = "0.1.0"

View File

@ -21,7 +21,6 @@
use crate::{
prelude::*,
thread::{kernel_thread::KernelThreadExt, Thread},
user_apps::{get_busybox_app, UserApp},
};
use process::Process;
@ -40,7 +39,6 @@ pub mod syscall;
pub mod thread;
pub mod time;
pub mod tty;
mod user_apps;
mod util;
pub mod vm;
@ -50,7 +48,7 @@ pub fn init() {
fs::initramfs::init(read_ramdisk_content()).unwrap();
}
pub fn init_thread() {
fn init_thread() {
println!(
"[kernel] Spawn init thread, tid = {}",
current_thread!().tid()
@ -68,15 +66,7 @@ pub fn init_thread() {
thread.tid()
);
// Run busybox ash
let UserApp {
executable_path: app_name,
argv,
envp,
} = get_busybox_app().unwrap();
println!("");
println!("BusyBox v1.35.0 built-in shell (ash)\n");
Process::spawn_user_process(app_name.clone(), argv, Vec::new());
run_busybox().expect("run busybox fails");
loop {
// We don't have preemptive scheduler now.
@ -95,3 +85,29 @@ pub fn run_first_process() -> ! {
Thread::spawn_kernel_thread(init_thread);
unreachable!()
}
fn run_busybox() -> Result<()> {
let executable_path = "/busybox/busybox";
let argv = ["sh", "-l"];
let envp = [
"SHELL=/bin/sh",
"PWD=/",
"LOGNAME=root",
"HOME=/",
"USER=root",
"PATH=/bin",
"OLDPWD=/",
];
let argv = argv
.into_iter()
.map(|arg| CString::new(arg).unwrap())
.collect();
let envp = envp
.into_iter()
.map(|env| CString::new(env).unwrap())
.collect();
println!("");
println!("BusyBox v1.35.0 built-in shell (ash)\n");
Process::spawn_user_process(executable_path, argv, envp);
Ok(())
}

View File

@ -1,6 +1,5 @@
use core::sync::atomic::{AtomicI32, Ordering};
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;
@ -11,11 +10,8 @@ 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, SeekFrom};
use crate::fs::fs_resolver::FsResolver;
use crate::prelude::*;
use crate::rights::Full;
use crate::thread::{thread_table, Thread};
@ -24,13 +20,13 @@ use crate::vm::vmar::Vmar;
use jinux_frame::sync::WaitQueue;
pub mod clone;
pub mod elf;
pub mod fifo_scheduler;
pub mod posix_thread;
pub mod process_filter;
pub mod process_group;
pub mod process_table;
pub mod process_vm;
pub mod program_loader;
pub mod rlimit;
pub mod signal;
pub mod status;
@ -141,11 +137,11 @@ impl Process {
/// init a user process and run the process
pub fn spawn_user_process(
filename: String,
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let process = Process::create_user_process(filename, argv, envp);
let process = Process::create_user_process(executable_path, argv, envp);
// FIXME: How to determine the fg process group?
let pgid = process.pgid();
// FIXME: tty should be a parameter?
@ -156,13 +152,12 @@ impl Process {
}
fn create_user_process(
executable_path: String,
executable_path: &str,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let user_process = Arc::new_cyclic(|weak_process_ref| {
let weak_process = weak_process_ref.clone();
let cloned_filename = Some(executable_path.clone());
let root_vmar = Vmar::<Full>::new_root().unwrap();
let fs = FsResolver::new();
let thread = Thread::new_posix_thread_from_executable(
@ -185,7 +180,7 @@ impl Process {
pid,
parent,
vec![thread],
cloned_filename,
Some(executable_path.to_string()),
Some(user_vm),
Arc::new(root_vmar),
Weak::new(),
@ -383,76 +378,3 @@ impl Process {
pub fn get_init_process() -> Option<Arc<Process>> {
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<CString>,
envp: Vec<CString>,
fs_resolver: &FsResolver,
root_vmar: &Vmar<Full>,
recursion_limit: usize,
) -> Result<ElfLoadInfo> {
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<CString>,
envp: Vec<CString>,
file_header_buffer: &[u8],
fs_resolver: &FsResolver,
root_vmar: &Vmar<Full>,
recursion_limit: usize,
) -> Result<ElfLoadInfo> {
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,
)
}

View File

@ -1,10 +1,9 @@
use alloc::string::String;
use jinux_frame::{cpu::CpuContext, user::UserSpace};
use crate::{
fs::fs_resolver::FsResolver,
prelude::*,
process::{setup_root_vmar, Process},
process::{program_loader::load_program_to_root_vmar, Process},
rights::Full,
thread::{allocate_tid, Thread},
vm::vmar::Vmar,
@ -16,7 +15,7 @@ pub trait PosixThreadExt {
fn new_posix_thread_from_executable(
root_vmar: &Vmar<Full>,
fs_resolver: &FsResolver,
executable_path: String,
executable_path: &str,
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
@ -28,17 +27,17 @@ impl PosixThreadExt for Thread {
fn new_posix_thread_from_executable(
root_vmar: &Vmar<Full>,
fs_resolver: &FsResolver,
executable_path: String,
executable_path: &str,
process: Weak<Process>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Arc<Self> {
let elf_load_info = setup_root_vmar(
executable_path.clone(),
let elf_load_info = load_program_to_root_vmar(
root_vmar,
executable_path.to_string(),
argv,
envp,
fs_resolver,
root_vmar,
1,
)
.unwrap();
@ -48,7 +47,7 @@ impl PosixThreadExt for Thread {
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_executable_path(&executable_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)

View File

@ -2,7 +2,7 @@
//! 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::process::program_loader::elf::init_stack::InitStack;
use crate::vm::perms::VmPerms;
use crate::vm::vmo::VmoRightsOp;
use crate::{
@ -25,9 +25,9 @@ use super::elf_segment_pager::ElfSegmentPager;
/// 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(
root_vmar: &Vmar<Full>,
file_header: &[u8],
elf_file: Arc<FileHandle>,
root_vmar: &Vmar<Full>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<ElfLoadInfo> {

View File

@ -0,0 +1,55 @@
pub mod elf;
mod shebang;
use crate::fs::file_handle::FileHandle;
use crate::fs::fs_resolver::{FsPath, FsResolver, AT_FDCWD};
use crate::fs::utils::AccessMode;
use crate::prelude::*;
use crate::rights::Full;
use crate::vm::vmar::Vmar;
use self::elf::{load_elf_to_root_vmar, ElfLoadInfo};
use self::shebang::parse_shebang_line;
/// Load an executable to root vmar, including loading programe image, preparing heap and stack,
/// initializing argv, envp and aux tables.
/// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables.
/// If the interpreter(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.
/// I guess for most cases, setting the recursion_limit as 1 should be enough.
/// because the interpreter is usually an elf binary(e.g., /bin/bash)
pub fn load_program_to_root_vmar(
root_vmar: &Vmar<Full>,
executable_path: String,
argv: Vec<CString>,
envp: Vec<CString>,
fs_resolver: &FsResolver,
recursion_limit: usize,
) -> Result<ElfLoadInfo> {
let fs_path = FsPath::new(AT_FDCWD, &executable_path)?;
let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?;
let file_header = {
// read the first page of file header
let mut file_header_buffer = [0u8; PAGE_SIZE];
file.read(&mut file_header_buffer)?;
file_header_buffer
};
if let Some(mut new_argv) = parse_shebang_line(&file_header)? {
if recursion_limit == 0 {
return_errno_with_message!(Errno::EINVAL, "the recursieve limit is reached");
}
new_argv.extend_from_slice(&argv);
let interpreter = new_argv[0].to_str()?.to_string();
return load_program_to_root_vmar(
root_vmar,
interpreter,
new_argv,
envp,
fs_resolver,
recursion_limit - 1,
);
}
let elf_file = Arc::new(FileHandle::new_inode_handle(file));
load_elf_to_root_vmar(root_vmar, &file_header, elf_file, argv, envp)
}

View File

@ -0,0 +1,31 @@
use crate::prelude::*;
/// Try to parse a buffer as a shebang line.
///
/// If the buffer starts with `#!` and its header is a valid shebang sequence,
/// then the function returns `Ok(Some(parts))`,
/// where `parts` is a `Vec` that contains the path of and the arguments for the interpreter.
/// If the buffer starts with `#!` but some error occurs while parsing the file,
/// then `Err(_)` is returned.
/// If the buffer does not start with `#!`, then `Ok(None)` is returned.
pub fn parse_shebang_line(file_header_buffer: &[u8]) -> Result<Option<Vec<CString>>> {
if !file_header_buffer.starts_with(b"#!") || !file_header_buffer.contains(&b'\n') {
// the file is not a shebang
return Ok(None);
}
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"
);
}
Ok(Some(shebang_argv))
}

View File

@ -4,7 +4,7 @@
use crate::prelude::*;
use super::{elf::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT};
use super::{process_vm::user_heap::USER_HEAP_SIZE_LIMIT, program_loader::elf::INIT_STACK_SIZE};
pub struct ResourceLimits {
rlimits: [RLimit64; RLIMIT_COUNT],

View File

@ -4,7 +4,7 @@ use super::{constants::*, SyscallReturn};
use crate::log_syscall_entry;
use crate::process::posix_thread::name::ThreadName;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::setup_root_vmar;
use crate::process::program_loader::load_program_to_root_vmar;
use crate::util::{read_cstring_from_user, read_val_from_user};
use crate::{prelude::*, syscall::SYS_EXECVE};
@ -44,7 +44,8 @@ pub fn sys_execve(
user_vm.set_default();
// load elf content to new vm space
let fs_resolver = &*current.fs().read();
let elf_load_info = setup_root_vmar(executable_path, argv, envp, fs_resolver, root_vmar, 1)?;
let elf_load_info =
load_program_to_root_vmar(root_vmar, executable_path, argv, envp, fs_resolver, 1)?;
debug!("load elf in execve succeeds");
// set signal disposition to default
current.sig_dispositions().lock().inherit();

View File

@ -1,50 +0,0 @@
use crate::prelude::*;
pub struct UserApp {
pub executable_path: String,
pub argv: Vec<CString>,
pub envp: Vec<CString>,
}
impl UserApp {
pub fn new(executable_path: &str) -> Result<Self> {
let app_name = String::from(executable_path);
let arg0 = CString::new(executable_path)?;
Ok(UserApp {
executable_path: app_name,
argv: vec![arg0],
envp: Vec::new(),
})
}
}
pub fn get_busybox_app() -> Result<UserApp> {
// busybox
let mut busybox = UserApp::new("/busybox/busybox")?;
// -l option means the busybox is running as logging shell
let argv = ["sh", "-l"];
let envp = [
"SHELL=/bin/sh",
"PWD=/",
"LOGNAME=root",
"HOME=/",
"USER=root",
"PATH=/bin",
"OLDPWD=/",
];
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 to_vec_cstring(raw_strs: &[&str]) -> Result<Vec<CString>> {
let mut res = Vec::new();
for raw_str in raw_strs {
let cstring = CString::new(*raw_str)?;
res.push(cstring);
}
Ok(res)
}