diff --git a/src/init.rs b/src/init.rs
index 26e4d011..9579d0ed 100644
--- a/src/init.rs
+++ b/src/init.rs
@@ -38,7 +38,6 @@ pub fn kinit() -> Result<(), Error> {
     let root = setup_root()?;
 
     let mut ioctx = IoContext::new(root);
-    let file = ioctx.open_exec(None, "/init")?;
 
     let devfs = devfs::root();
 
@@ -54,9 +53,8 @@ pub fn kinit() -> Result<(), Error> {
     let stderr = stdout.clone();
 
     {
-        // XXX
         let (user_init, user_init_main) =
-            proc::exec::load_elf("init", file, &["/init", "xxx"], &[])?;
+            proc::exec::load(&mut ioctx, "/init", &["/init", "xxx"], &[])?;
 
         let mut io = user_init.io.lock();
         io.set_ioctx(ioctx);
diff --git a/src/main.rs b/src/main.rs
index bc37fb16..fc52cb9e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,9 @@
     allocator_api,
     trait_alias,
     strict_provenance,
-    slice_ptr_get
+    slice_ptr_get,
+    slice_split_once,
+    iter_collect_into
 )]
 #![allow(
     clippy::new_without_default,
diff --git a/src/proc/exec.rs b/src/proc/exec.rs
index 823d1b28..503cb543 100644
--- a/src/proc/exec.rs
+++ b/src/proc/exec.rs
@@ -3,11 +3,13 @@ use core::{alloc::Layout, ptr::NonNull};
 
 use abi::{
     error::Error,
+    io::SeekFrom,
     pass::{Place, Placer},
+    path::Path,
     process::ProgramArgumentInner,
 };
-use alloc::{string::String, sync::Arc};
-use vfs::FileRef;
+use alloc::{string::String, sync::Arc, vec::Vec};
+use vfs::{FileRef, IoContext, Read, Seek};
 
 use crate::{
     mem::{
@@ -159,18 +161,49 @@ fn setup_binary<S: Into<String>>(
         context,
         Some(image),
     ))
-    // Ok(Process::new_with_context(name, Some(space), context))
 }
 
-/// Loads an ELF bianary from `file` and sets up all the necessary data/argument memory
-pub fn load_elf<S: Into<String>>(
-    name: S,
+fn load_binary(
+    head: &[u8],
     file: FileRef,
+    space: &ProcessAddressSpace,
+) -> Result<ProcessImage, Error> {
+    if head.starts_with(b"\x7FELF") {
+        proc::elf::load_elf_from_file(space, file)
+    } else {
+        Err(Error::UnrecognizedExecutable)
+    }
+}
+
+pub fn load<P: AsRef<Path>>(
+    ioctx: &mut IoContext,
+    path: P,
     args: &[&str],
     envs: &[&str],
 ) -> Result<(Arc<Process>, Arc<Thread>), Error> {
-    let space = ProcessAddressSpace::new()?;
-    let image = proc::elf::load_elf_from_file(&space, file)?;
+    let mut head = [0; 256];
+    let path = path.as_ref();
+    let file = ioctx.open_exec(None, path)?;
 
-    setup_binary(name, space, image, args, envs)
+    file.seek(SeekFrom::Start(0))?;
+    let count = file.read(&mut head)?;
+    let head = &head[..count];
+
+    if let Some(shebang) = head.strip_prefix(b"#!") && let Some((shebang, _)) = shebang.split_once(|&ch| ch == b'\n') {
+        let shebang = core::str::from_utf8(shebang).map_err(|_| Error::InvalidFile)?;
+        let mut shebang_args = shebang.split(' ').collect::<Vec<_>>();
+        if shebang_args.is_empty() || shebang_args.len() >= 8 {
+            return Err(Error::UnrecognizedExecutable);
+        }
+        shebang_args.extend_from_slice(args);
+
+        return load(ioctx, shebang_args[0], &shebang_args, envs);
+    }
+
+    file.seek(SeekFrom::Start(0))?;
+
+    let space = ProcessAddressSpace::new()?;
+    let image = load_binary(head, file, &space)?;
+
+    setup_binary(path.display(), space, image, args, envs)
 }
diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs
index 797aa62e..7d8b459d 100644
--- a/src/syscall/mod.rs
+++ b/src/syscall/mod.rs
@@ -267,14 +267,10 @@ fn syscall_handler(func: SyscallFunction, args: &[u64]) -> Result<usize, Error>
             let options = arg_user_ref::<SpawnOptions>(args[0] as usize)?;
 
             run_with_io(process, |mut io| {
-                // let node = io.ioctx().find(None, options.program, true, true)?;
-
                 // Setup a new process from the file
-                let file = io.ioctx().open_exec(None, options.program)?;
-                // let file = node.open(OpenOptions::READ)?;
-                let (child_process, child_main) = proc::exec::load_elf(
+                let (child_process, child_main) = proc::exec::load(
+                    io.ioctx(),
                     options.program,
-                    file,
                     options.arguments,
                     options.environment,
                 )?;