Quellcode durchsuchen

Merge pull request #35 from greatbridf/interpreter

Add interpreter support for the `execve` syscall
Heinz vor 7 Monaten
Ursprung
Commit
6d9027b71b

+ 1 - 1
src/driver/sbi_console.rs

@@ -1,4 +1,4 @@
-use crate::kernel::{block::make_device, CharDevice, CharDeviceType, Terminal, TerminalDevice};
+use crate::kernel::{Terminal, TerminalDevice};
 use alloc::sync::Arc;
 use eonix_log::ConsoleWrite;
 

+ 1 - 1
src/fs/ext4.rs

@@ -12,7 +12,7 @@ use crate::{
             mount::{register_filesystem, Mount, MountCreator},
             s_isdir, s_isreg,
             vfs::Vfs,
-            DevId, FsContext, TimeSpec,
+            DevId, FsContext,
         },
     },
     path::Path,

+ 1 - 0
src/kernel/console.rs

@@ -67,6 +67,7 @@ macro_rules! println_fatal {
     };
 }
 
+#[allow(unused_macros)]
 macro_rules! println_trace {
     ($feat:literal) => {
         #[deny(unexpected_cfgs)]

+ 5 - 7
src/kernel/syscall/procops.rs

@@ -151,22 +151,20 @@ fn get_strings(mut ptr_strings: UserPointer<'_, PtrT>) -> KResult<Vec<CString>>
 #[eonix_macros::define_syscall(SYS_EXECVE)]
 fn execve(exec: *const u8, argv: *const PtrT, envp: *const PtrT) -> KResult<SyscallNoReturn> {
     let exec = UserString::new(exec)?;
+    let exec = exec.as_cstr().to_owned();
+
     let argv = get_strings(UserPointer::new(argv)?)?;
     let envp = get_strings(UserPointer::new(envp)?)?;
 
-    let dentry = Dentry::open(
-        &thread.fs_context,
-        Path::new(exec.as_cstr().to_bytes())?,
-        true,
-    )?;
-
+    let dentry = Dentry::open(&thread.fs_context, Path::new(exec.as_bytes())?, true)?;
     if !dentry.is_valid() {
         Err(ENOENT)?;
     }
 
     // TODO: When `execve` is called by one of the threads in a process, the other threads
     //       should be terminated and `execve` is performed in the thread group leader.
-    let load_info = ProgramLoader::parse(dentry.clone())?.load(argv, envp)?;
+    let load_info =
+        ProgramLoader::parse(&thread.fs_context, exec, dentry.clone(), argv, envp)?.load()?;
 
     if let Some(robust_list) = thread.get_robust_list() {
         let _ = Task::block_on(robust_list.wake_all());

+ 73 - 11
src/kernel/task/loader/mod.rs

@@ -1,11 +1,12 @@
 use crate::io::ByteBuffer;
 use crate::kernel::constants::ENOEXEC;
 use crate::kernel::task::loader::elf::ELF;
+use crate::kernel::vfs::FsContext;
+use crate::path::Path;
 use crate::{
     kernel::{mem::MMList, vfs::dentry::Dentry},
     prelude::*,
 };
-
 use alloc::{ffi::CString, sync::Arc};
 use eonix_mm::address::VAddr;
 
@@ -26,27 +27,88 @@ enum Object {
 }
 
 pub struct ProgramLoader {
+    args: Vec<CString>,
+    envs: Vec<CString>,
     object: Object,
 }
 
 impl ProgramLoader {
-    pub fn parse(file: Arc<Dentry>) -> KResult<Self> {
-        let mut magic = [0u8; 4];
-        file.read(&mut ByteBuffer::new(magic.as_mut_slice()), 0)?;
+    pub fn parse(
+        fs_context: &FsContext,
+        mut exec_path: CString,
+        mut file: Arc<Dentry>,
+        mut args: Vec<CString>,
+        envs: Vec<CString>,
+    ) -> KResult<Self> {
+        const RECURSION_LIMIT: usize = 4;
+        let mut nrecur = 0;
+
+        let object = loop {
+            if nrecur >= RECURSION_LIMIT {
+                return Err(ENOEXEC);
+            }
+
+            let mut magic = [0; 4];
+            file.read(&mut ByteBuffer::new(magic.as_mut_slice()), 0)?;
+
+            match magic {
+                [b'#', b'!', ..] => {
+                    let mut interpreter_line = [0; 256];
+                    let nread = file.read(&mut ByteBuffer::new(&mut interpreter_line), 0)?;
+
+                    // There is a tiny time gap between reading the magic number and
+                    // reading the interpreter line, so we need to check if the line
+                    // still starts with a shebang.
+                    if !interpreter_line.starts_with(b"#!") {
+                        return Err(ENOEXEC);
+                    }
+
+                    let line = &interpreter_line[2..nread];
+                    let line = line.split(|&b| b == b'\n' || b == b'\0').next().unwrap();
+
+                    let interpreter_name;
+                    let mut interpreter_arg = None;
+                    match line.iter().position(|&b| b == b' ') {
+                        Some(blank_pos) => {
+                            interpreter_name = CString::new(&line[..blank_pos]).unwrap();
+                            interpreter_arg = Some(CString::new(&line[blank_pos + 1..]).unwrap());
+                        }
+                        None => interpreter_name = CString::new(line).unwrap(),
+                    }
+
+                    let path = Path::new(interpreter_name.as_bytes())?;
+                    file = Dentry::open(fs_context, path, true)?;
+
+                    args.insert(0, interpreter_name.clone());
+                    if let Some(arg) = interpreter_arg {
+                        args.insert(1, arg);
+                    }
+
+                    if args.len() > 2 {
+                        args[2] = exec_path;
+                    } else {
+                        args.push(exec_path);
+                    }
+
+                    exec_path = interpreter_name;
+                }
+                ELF_MAGIC => break ELF::parse(file)?,
+                _ => return Err(ENOEXEC),
+            }
 
-        let object = match magic {
-            ELF_MAGIC => ELF::parse(file),
-            _ => Err(ENOEXEC),
-        }?;
+            nrecur += 1;
+        };
 
         Ok(ProgramLoader {
+            args,
+            envs,
             object: Object::ELF(object),
         })
     }
 
-    pub fn load(&self, args: Vec<CString>, envs: Vec<CString>) -> KResult<LoadInfo> {
-        match &self.object {
-            Object::ELF(elf) => elf.load(args, envs),
+    pub fn load(self) -> KResult<LoadInfo> {
+        match self.object {
+            Object::ELF(elf) => elf.load(self.args, self.envs),
         }
     }
 }

+ 0 - 1
src/kernel/vfs/inode.rs

@@ -15,7 +15,6 @@ use core::{
 };
 use eonix_runtime::task::Task;
 use eonix_sync::RwLock;
-use posix_types::namei::RenameFlags;
 use posix_types::stat::StatX;
 
 pub type Ino = u64;

+ 0 - 13
src/kernel/vfs/mod.rs

@@ -34,13 +34,6 @@ pub fn s_islnk(mode: Mode) -> bool {
     (mode & S_IFMT) == S_IFLNK
 }
 
-#[derive(Clone, Copy, Default)]
-#[repr(C)]
-pub struct TimeSpec {
-    pub sec: u64,
-    pub nsec: u64,
-}
-
 pub struct FsContext {
     pub fsroot: Arc<Dentry>,
     pub cwd: Spin<Arc<Dentry>>,
@@ -55,12 +48,6 @@ static GLOBAL_FS_CONTEXT: LazyLock<Arc<FsContext>> = LazyLock::new(|| {
     })
 });
 
-impl TimeSpec {
-    pub const fn default() -> Self {
-        Self { sec: 0, nsec: 0 }
-    }
-}
-
 impl FsContext {
     pub fn global() -> &'static Arc<Self> {
         &GLOBAL_FS_CONTEXT

+ 8 - 17
src/lib.rs

@@ -163,19 +163,14 @@ async fn init_process(early_kstack: PRange) {
         )
         .unwrap();
 
-        let init_names = [
-            &b"/sbin/init"[..],
-            &b"/init"[..],
-            &b"/bin/busybox"[..],
-            &b"/mnt/busybox"[..],
-        ];
+        let init_names = [&b"/init"[..], &b"/sbin/init"[..], &b"/mnt/initsh"[..]];
 
         let mut init_name = None;
         let mut init = None;
-        for name in &init_names {
+        for name in init_names {
             if let Ok(dentry) = Dentry::open(fs_context, Path::new(name).unwrap(), true) {
                 if dentry.is_valid() {
-                    init_name = Some(*name);
+                    init_name = Some(CString::new(name).unwrap());
                     init = Some(dentry);
                     break;
                 }
@@ -185,11 +180,7 @@ async fn init_process(early_kstack: PRange) {
         let init = init.expect("No init binary found in the system.");
         let init_name = init_name.unwrap();
 
-        let argv = vec![
-            CString::new(init_name).unwrap(),
-            CString::new("sh").unwrap(),
-            CString::new("/mnt/initsh").unwrap(),
-        ];
+        let argv = vec![init_name.clone()];
 
         let envp = vec![
             CString::new("LANG=C").unwrap(),
@@ -198,10 +189,10 @@ async fn init_process(early_kstack: PRange) {
             CString::new("PWD=/").unwrap(),
         ];
 
-        ProgramLoader::parse(init.clone())
-            .unwrap()
-            .load(argv, envp)
-            .unwrap()
+        ProgramLoader::parse(fs_context, init_name, init.clone(), argv, envp)
+            .expect("Failed to parse init program")
+            .load()
+            .expect("Failed to load init program")
     };
 
     let thread_builder = ThreadBuilder::new()