瀏覽代碼

feat(loader): rewrite loader and support dynamic loading

zhuowei shao 8 月之前
父節點
當前提交
1ff75b9e8f

+ 35 - 12
Cargo.lock

@@ -13,6 +13,12 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "align_ext"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1c330e503236d0b06386ae6cc42a513ef1ccc23c52b603c1b52f018564faf44"
+
 [[package]]
 name = "arch"
 version = "0.1.0"
@@ -69,9 +75,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "either"
-version = "1.13.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
 [[package]]
 name = "eonix_hal"
@@ -110,6 +116,7 @@ name = "eonix_kernel"
 version = "0.1.0"
 dependencies = [
  "acpi",
+ "align_ext",
  "arch",
  "atomic_unique_refcell",
  "bitflags",
@@ -128,6 +135,7 @@ dependencies = [
  "pointers",
  "posix_types",
  "slab_allocator",
+ "xmas-elf",
 ]
 
 [[package]]
@@ -249,9 +257,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.22"
+version = "0.4.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 
 [[package]]
 name = "memoffset"
@@ -272,18 +280,18 @@ version = "0.1.0"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.92"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.37"
+version = "1.0.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
 dependencies = [
  "proc-macro2",
 ]
@@ -299,9 +307,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.89"
+version = "2.0.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e"
+checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -310,6 +318,21 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.14"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "xmas-elf"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18245fcbb8b3de8dd198ce7944fdd4096986fd6cd306b0fcfa27df817bd545d6"
+dependencies = [
+ "zero",
+]
+
+[[package]]
+name = "zero"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"

+ 2 - 0
Cargo.toml

@@ -29,6 +29,8 @@ bitflags = "2.6.0"
 intrusive-collections = "0.9.7"
 itertools = { version = "0.13.0", default-features = false }
 acpi = "5.2.0"
+align_ext = "0.1.0"
+xmas-elf = "0.10.0"
 
 [features]
 default = []

+ 1 - 1
crates/eonix_hal/src/arch/x86_64/trap/trap_context.rs

@@ -37,7 +37,7 @@ impl TrapContext {
             13 => Fault::BadAccess,
             14 => {
                 let mut error_code = PageFaultErrorCode::empty();
-                if self.errcode & 1 != 0 {
+                if self.errcode & 1 == 0 {
                     error_code |= PageFaultErrorCode::NonPresent;
                 }
 

+ 4 - 0
init_script.sh

@@ -32,6 +32,7 @@ echo -n -e "deploying busybox... " >&2
 
 do_or_freeze $BUSYBOX mkdir -p /bin
 do_or_freeze $BUSYBOX --install -s /bin
+do_or_freeze $BUSYBOX mkdir -p /lib
 
 export PATH="/bin"
 
@@ -40,6 +41,9 @@ echo ok >&2
 do_or_freeze mkdir -p /etc /root /proc
 do_or_freeze mount -t procfs proc proc
 
+cp /mnt/ld-musl-i386.so.1 /lib/ld-musl-i386.so.1
+ln -s /lib/ld-musl-i386.so.1 /lib/libc.so
+
 cat > /etc/passwd <<EOF
 root:x:0:0:root:/root:/mnt/busybox sh
 EOF

+ 3 - 1
script/build-img.sh

@@ -18,13 +18,15 @@ fi
 
 $SUDO cp ./user-programs/init.out build/mnt/init
 $SUDO cp ./user-programs/int.out build/mnt/int
+$SUDO cp ./user-programs/dynamic_test build/mnt/dynamic_test
 $SUDO cp ./user-programs/busybox build/mnt/busybox
 $SUDO cp ./user-programs/busybox-minimal build/mnt/busybox_
+$SUDO cp ./user-programs/ld-musl-i386.so.1 build/mnt/ld-musl-i386.so.1
 $SUDO cp ./init_script.sh build/mnt/initsh
 
 # Add your custom files here
 
-$SUDO cp -r $HOME/.local/i486-linux-musl-cross build/mnt/
+# $SUDO cp -r $HOME/.local/i486-linux-musl-cross build/mnt/
 
 # End of custom files
 

+ 0 - 349
src/elf.rs

@@ -1,349 +0,0 @@
-use crate::{
-    io::{ByteBuffer, UninitBuffer},
-    kernel::{
-        constants::ENOEXEC,
-        mem::{FileMapping, MMList, Mapping, Permission},
-        vfs::dentry::Dentry,
-    },
-    prelude::*,
-};
-use alloc::{ffi::CString, sync::Arc};
-use bitflags::bitflags;
-use eonix_mm::address::{Addr as _, AddrOps as _, VAddr};
-
-#[repr(u8)]
-#[allow(dead_code)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum ElfFormat {
-    Elf32 = 1,
-    Elf64 = 2,
-}
-
-#[repr(u8)]
-#[allow(dead_code)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum ElfEndian {
-    Little = 1,
-    Big = 2,
-}
-
-#[repr(u8)]
-#[allow(dead_code)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum ElfABI {
-    // SystemV = 0,
-    Linux = 3,
-}
-
-#[repr(u16)]
-#[allow(dead_code)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum ElfType {
-    Relocatable = 1,
-    Executable = 2,
-    Dynamic = 3,
-    Core = 4,
-}
-
-#[repr(u16)]
-#[allow(dead_code)]
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum ElfArch {
-    X86 = 0x03,
-    Arm = 0x28,
-    IA64 = 0x32,
-    X86_64 = 0x3e,
-    AArch64 = 0xb7,
-    RiscV = 0xf3,
-}
-
-bitflags! {
-    #[derive(Default, Clone, Copy)]
-    pub struct Elf32PhFlags: u32 {
-        const Exec = 1;
-        const Write = 2;
-        const Read = 4;
-    }
-
-    #[derive(Default, Clone, Copy)]
-    pub struct Elf32ShFlags: u32 {
-        const Write = 1;
-        const Alloc = 2;
-        const Exec = 4;
-        const MaskProc = 0xf0000000;
-    }
-}
-
-#[allow(dead_code)]
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
-pub enum Elf32PhType {
-    #[default]
-    Null = 0,
-    Load = 1,
-    Dynamic = 2,
-    Interp = 3,
-    Note = 4,
-    Shlib = 5,
-    Phdr = 6,
-    Tls = 7,
-    Loos = 0x60000000,
-    Hios = 0x6fffffff,
-    Loproc = 0x70000000,
-    Hiproc = 0x7fffffff,
-}
-
-#[allow(dead_code)]
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
-pub enum Elf32ShType {
-    #[default]
-    Null = 0,
-    ProgBits = 1,
-    SymTab = 2,
-    StrTab = 3,
-    Rela = 4,
-    Hash = 5,
-    Dynamic = 6,
-    Note = 7,
-    NoBits = 8,
-    Rel = 9,
-    Shlib = 10,
-    DynSym = 11,
-    InitArray = 14,
-    FiniArray = 15,
-    PreInitArray = 16,
-    Group = 17,
-    SymTabShndx = 18,
-    Loos = 0x60000000,
-    Hios = 0x6fffffff,
-    Loproc = 0x70000000,
-    Hiproc = 0x7fffffff,
-}
-
-#[repr(C, packed)]
-#[derive(Clone, Copy)]
-pub struct Elf32Header {
-    /// ELF magic number: 0x7f, "ELF"
-    pub magic: [u8; 4],
-    pub format: ElfFormat,
-    pub endian: ElfEndian,
-    /// ELF version, should be 1
-    pub version: u8,
-    pub abi: ElfABI,
-    pub abi_version: u8,
-    padding: [u8; 7],
-    pub elf_type: ElfType,
-    pub arch: ElfArch,
-    /// ELF version, should be 1
-    pub version2: u32,
-    pub entry: u32,
-    pub ph_offset: u32,
-    pub sh_offset: u32,
-    pub flags: u32,
-    pub eh_size: u16,
-    pub ph_entry_size: u16,
-    pub ph_entry_count: u16,
-    pub sh_entry_size: u16,
-    pub sh_entry_count: u16,
-    pub sh_str_index: u16,
-}
-
-#[repr(C)]
-#[derive(Default, Clone, Copy)]
-pub struct Elf32PhEntry {
-    pub ph_type: Elf32PhType,
-    pub offset: u32,
-    pub vaddr: u32,
-    pub paddr: u32,
-    pub file_size: u32,
-    pub mem_size: u32,
-    pub flags: Elf32PhFlags,
-    /// `0` and `1` for no alignment, otherwise power of `2`
-    pub align: u32,
-}
-
-#[repr(C)]
-#[derive(Default, Clone, Copy)]
-pub struct Elf32ShEntry {
-    pub name_offset: u32,
-    pub sh_type: Elf32ShType,
-    pub flags: Elf32ShFlags,
-    pub addr: u32,
-    pub offset: u32,
-    pub size: u32,
-    pub link: u32,
-    pub info: u32,
-    pub addr_align: u32,
-    pub entry_size: u32,
-}
-
-#[allow(dead_code)]
-pub struct ParsedElf32 {
-    entry: u32,
-    file: Arc<Dentry>,
-    phents: Vec<Elf32PhEntry>,
-    shents: Vec<Elf32ShEntry>,
-}
-
-const ELF_MAGIC: [u8; 4] = *b"\x7fELF";
-
-impl Elf32Header {
-    fn check_valid(&self) -> bool {
-        self.magic == ELF_MAGIC
-            && self.version == 1
-            && self.version2 == 1
-            && self.eh_size as usize == size_of::<Elf32Header>()
-            && self.ph_entry_size as usize == size_of::<Elf32PhEntry>()
-            && self.sh_entry_size as usize == size_of::<Elf32ShEntry>()
-    }
-}
-
-impl ParsedElf32 {
-    pub fn parse(file: Arc<Dentry>) -> KResult<Self> {
-        let mut header = UninitBuffer::<Elf32Header>::new();
-        file.read(&mut header, 0)?;
-
-        let header = header.assume_init().map_err(|_| ENOEXEC)?;
-        if !header.check_valid() {
-            return Err(ENOEXEC);
-        }
-
-        // TODO: Use `UninitBuffer` for `phents` and `shents`.
-        let mut phents = vec![Elf32PhEntry::default(); header.ph_entry_count as usize];
-        let nread = file.read(
-            &mut ByteBuffer::from(phents.as_mut_slice()),
-            header.ph_offset as usize,
-        )?;
-        if nread != header.ph_entry_count as usize * size_of::<Elf32PhEntry>() {
-            return Err(ENOEXEC);
-        }
-
-        let mut shents = vec![Elf32ShEntry::default(); header.sh_entry_count as usize];
-        let nread = file.read(
-            &mut ByteBuffer::from(shents.as_mut_slice()),
-            header.sh_offset as usize,
-        )?;
-        if nread != header.sh_entry_count as usize * size_of::<Elf32ShEntry>() {
-            return Err(ENOEXEC);
-        }
-
-        Ok(Self {
-            entry: header.entry,
-            file,
-            phents,
-            shents,
-        })
-    }
-
-    /// Load the ELF file into memory. Return the entry point address and the memory list containing the program data.
-    ///
-    /// We clear the user space and load the program headers into memory.
-    /// Can't make a way back if failed from now on.
-    ///
-    /// # Return
-    /// `(entry_ip, sp, mm_list)`
-    pub fn load(self, args: Vec<CString>, envs: Vec<CString>) -> KResult<(VAddr, VAddr, MMList)> {
-        let mm_list = MMList::new();
-
-        let mut data_segment_end = VAddr::NULL;
-        for phent in self
-            .phents
-            .into_iter()
-            .filter(|ent| ent.ph_type == Elf32PhType::Load)
-        {
-            let vaddr_start = VAddr::from(phent.vaddr as usize);
-            let vmem_vaddr_end = vaddr_start + phent.mem_size as usize;
-            let load_vaddr_end = vaddr_start + phent.file_size as usize;
-
-            let vaddr = vaddr_start.floor();
-            let vmem_len = vmem_vaddr_end.ceil() - vaddr;
-            let file_len = load_vaddr_end.ceil() - vaddr;
-            let file_offset = phent.offset as usize & !0xfff;
-
-            let permission = Permission {
-                write: phent.flags.contains(Elf32PhFlags::Write),
-                execute: phent.flags.contains(Elf32PhFlags::Exec),
-            };
-
-            if file_len != 0 {
-                let real_file_length = load_vaddr_end - vaddr;
-                mm_list.mmap_fixed(
-                    vaddr,
-                    file_len,
-                    Mapping::File(FileMapping::new(
-                        self.file.clone(),
-                        file_offset,
-                        real_file_length,
-                    )),
-                    permission,
-                )?;
-            }
-
-            if vmem_len > file_len {
-                mm_list.mmap_fixed(
-                    vaddr + file_len,
-                    vmem_len - file_len,
-                    Mapping::Anonymous,
-                    permission,
-                )?;
-            }
-
-            if vaddr + vmem_len > data_segment_end {
-                data_segment_end = vaddr + vmem_len;
-            }
-        }
-
-        mm_list.register_break(data_segment_end + 0x10000);
-
-        // Map stack area
-        mm_list.mmap_fixed(
-            VAddr::from(0xc0000000 - 0x800000), // Stack bottom is at 0xc0000000
-            0x800000,                           // 8MB stack size
-            Mapping::Anonymous,
-            Permission {
-                write: true,
-                execute: false,
-            },
-        )?;
-
-        let mut sp = VAddr::from(0xc0000000); // Current stack top
-        let arg_addrs = push_strings(&mm_list, &mut sp, args)?;
-        let env_addrs = push_strings(&mm_list, &mut sp, envs)?;
-
-        let mut longs = vec![];
-        longs.push(arg_addrs.len() as u32); // argc
-        longs.extend(arg_addrs.into_iter()); // args
-        longs.push(0); // null
-        longs.extend(env_addrs.into_iter()); // envs
-        longs.push(0); // null
-        longs.push(0); // AT_NULL
-        longs.push(0); // AT_NULL
-
-        sp = sp - longs.len() * size_of::<u32>();
-        sp = sp.floor_to(16);
-
-        mm_list.access_mut(sp, longs.len() * size_of::<u32>(), |offset, data| {
-            data.copy_from_slice(unsafe {
-                core::slice::from_raw_parts(
-                    longs.as_ptr().byte_add(offset) as *const u8,
-                    data.len(),
-                )
-            })
-        })?;
-
-        Ok((VAddr::from(self.entry as usize), sp, mm_list))
-    }
-}
-
-fn push_strings(mm_list: &MMList, sp: &mut VAddr, strings: Vec<CString>) -> KResult<Vec<u32>> {
-    let mut addrs = vec![];
-    for string in strings {
-        let len = string.as_bytes_with_nul().len();
-        *sp = *sp - len;
-        mm_list.access_mut(*sp, len, |offset, data| {
-            data.copy_from_slice(&string.as_bytes_with_nul()[offset..offset + data.len()])
-        })?;
-        addrs.push(sp.addr() as u32);
-    }
-
-    Ok(addrs)
-}

+ 98 - 1
src/kernel/mem/mm_list.rs

@@ -29,6 +29,7 @@ pub static EMPTY_PAGE: LazyLock<Page> = LazyLock::new(|| Page::zeroed());
 
 #[derive(Debug, Clone, Copy)]
 pub struct Permission {
+    pub read: bool,
     pub write: bool,
     pub execute: bool,
 }
@@ -170,6 +171,91 @@ impl MMListInner<'_> {
         Ok(pages_to_free)
     }
 
+    fn protect(&mut self, start: VAddr, len: usize, permission: Permission) -> KResult<()> {
+        assert_eq!(start.floor(), start);
+        assert!(len != 0);
+
+        let end = (start + len).ceil();
+        let range_to_protect = VRange::new(start, end);
+        if !range_to_protect.is_user() {
+            return Err(EINVAL);
+        }
+
+        let mut left_area = None;
+        let mut right_area = None;
+        let mut mid_area = None;
+
+        self.areas.retain(|area| {
+            let Some((left, mid, right)) = area.range().mask_with_checked(&range_to_protect) else {
+                return true;
+            };
+
+            for pte in self.page_table.iter_user(mid) {
+                let mut page_attr = pte.get_attr().as_page_attr().expect("Not a page attribute");
+
+                page_attr.set(PageAttribute::READ, permission.read);
+                page_attr.set(PageAttribute::WRITE, permission.write);
+                page_attr.set(PageAttribute::EXECUTE, permission.execute);
+
+                pte.set_attr(page_attr.into());
+            }
+
+            match (left, right) {
+                (None, None) => {}
+                (Some(left), None) => {
+                    assert!(left_area.is_none());
+                    let (Some(left), Some(right)) = area.clone().split(left.end()) else {
+                        unreachable!("`left.end()` is within the area");
+                    };
+
+                    left_area = Some(left);
+                    mid_area = Some(right);
+                }
+                (None, Some(right)) => {
+                    assert!(right_area.is_none());
+                    let (Some(left), Some(right)) = area.clone().split(right.start()) else {
+                        unreachable!("`right.start()` is within the area");
+                    };
+
+                    mid_area = Some(left);
+                    right_area = Some(right);
+                }
+                (Some(left), Some(right)) => {
+                    assert!(left_area.is_none());
+                    assert!(right_area.is_none());
+                    let (Some(left), Some(mid)) = area.clone().split(left.end()) else {
+                        unreachable!("`left.end()` is within the area");
+                    };
+
+                    let (Some(mid), Some(right)) = mid.split(right.start()) else {
+                        unreachable!("`right.start()` is within the area");
+                    };
+
+                    left_area = Some(left);
+                    right_area = Some(right);
+                    mid_area = Some(mid);
+                }
+            }
+
+            false
+        });
+
+        assert!(mid_area.is_some());
+
+        if let Some(mut mid) = mid_area {
+            mid.permission = permission;
+            self.areas.insert(mid);
+        }
+        if let Some(front) = left_area {
+            self.areas.insert(front);
+        }
+        if let Some(back) = right_area {
+            self.areas.insert(back);
+        }
+
+        Ok(())
+    }
+
     fn mmap(
         &mut self,
         at: VAddr,
@@ -178,7 +264,7 @@ impl MMListInner<'_> {
         permission: Permission,
     ) -> KResult<()> {
         assert_eq!(at.floor(), at);
-        assert_eq!(len & 0xfff, 0);
+        assert_eq!(len & (PAGE_SIZE - 1), 0);
         let range = VRange::new(at, at + len);
 
         // We are doing a area marker insertion.
@@ -361,6 +447,15 @@ impl MMList {
         Ok(())
     }
 
+    pub async fn protect(&self, start: VAddr, len: usize, prot: Permission) -> KResult<()> {
+        self.inner.borrow().lock().await.protect(start, len, prot)?;
+
+        // flush the tlb due to the pte attribute changes
+        self.flush_user_tlbs().await;
+
+        Ok(())
+    }
+
     pub fn mmap_hint(
         &self,
         hint: VAddr,
@@ -423,6 +518,7 @@ impl MMList {
                 break_start,
                 Mapping::Anonymous,
                 Permission {
+                    read: true,
                     write: true,
                     execute: false,
                 },
@@ -442,6 +538,7 @@ impl MMList {
         inner.page_table.set_anonymous(
             range_to_grow,
             Permission {
+                read: true,
                 write: true,
                 execute: false,
             },

+ 2 - 1
src/kernel/mem/mm_list/mapping.rs

@@ -1,5 +1,6 @@
 use crate::kernel::vfs::dentry::Dentry;
 use alloc::sync::Arc;
+use eonix_mm::paging::PAGE_SIZE;
 
 #[derive(Debug, Clone)]
 pub struct FileMapping {
@@ -17,7 +18,7 @@ pub enum Mapping {
 
 impl FileMapping {
     pub fn new(file: Arc<Dentry>, offset: usize, length: usize) -> Self {
-        assert_eq!(offset & 0xfff, 0);
+        assert_eq!(offset & (PAGE_SIZE - 1), 0);
         Self {
             file,
             offset,

+ 26 - 2
src/kernel/syscall/mm.rs

@@ -7,7 +7,9 @@ use crate::{
     },
     prelude::*,
 };
+use align_ext::AlignExt;
 use eonix_mm::address::{Addr as _, AddrOps as _, VAddr};
+use eonix_mm::paging::PAGE_SIZE;
 use eonix_runtime::task::Task;
 
 impl FromSyscallArg for UserMmapProtocol {
@@ -46,7 +48,7 @@ fn mmap_pgoff(
         return Err(EINVAL);
     }
 
-    let len = (len + 0xfff) & !0xfff;
+    let len = len.align_up(PAGE_SIZE);
     check_impl(flags.contains(UserMmapFlags::MAP_ANONYMOUS), ENOMEM)?;
     check_impl(flags.contains(UserMmapFlags::MAP_PRIVATE), EINVAL)?;
     if fd != u32::MAX || pgoffset != 0 {
@@ -70,6 +72,7 @@ fn mmap_pgoff(
             len,
             Mapping::Anonymous,
             Permission {
+                read: prot.contains(UserMmapProtocol::PROT_READ),
                 write: prot.contains(UserMmapProtocol::PROT_WRITE),
                 execute: prot.contains(UserMmapProtocol::PROT_EXEC),
             },
@@ -80,6 +83,7 @@ fn mmap_pgoff(
             len,
             Mapping::Anonymous,
             Permission {
+                read: prot.contains(UserMmapProtocol::PROT_READ),
                 write: prot.contains(UserMmapProtocol::PROT_WRITE),
                 execute: prot.contains(UserMmapProtocol::PROT_EXEC),
             },
@@ -96,7 +100,7 @@ fn munmap(addr: usize, len: usize) -> KResult<usize> {
         return Err(EINVAL);
     }
 
-    let len = (len + 0xfff) & !0xfff;
+    let len = len.align_up(PAGE_SIZE);
     Task::block_on(thread.process.mm_list.unmap(addr, len)).map(|_| 0)
 }
 
@@ -111,4 +115,24 @@ fn madvise(_addr: usize, _len: usize, _advice: u32) -> KResult<()> {
     Ok(())
 }
 
+#[eonix_macros::define_syscall(0x7d)]
+fn mprotect(addr: usize, len: usize, prot: UserMmapProtocol) -> KResult<()> {
+    let addr = VAddr::from(addr);
+    if !addr.is_page_aligned() || len == 0 {
+        return Err(EINVAL);
+    }
+
+    let len = len.align_up(PAGE_SIZE);
+
+    Task::block_on(thread.process.mm_list.protect(
+        addr,
+        len,
+        Permission {
+            read: prot.contains(UserMmapProtocol::PROT_READ),
+            write: prot.contains(UserMmapProtocol::PROT_WRITE),
+            execute: prot.contains(UserMmapProtocol::PROT_EXEC),
+        },
+    ))
+}
+
 pub fn keep_alive() {}

+ 6 - 8
src/kernel/syscall/procops.rs

@@ -1,6 +1,5 @@
 use super::sysinfo::TimeVal;
 use super::SyscallNoReturn;
-use crate::elf::ParsedElf32;
 use crate::io::Buffer;
 use crate::kernel::constants::{EINVAL, ENOENT, ENOTDIR, ERANGE, ESRCH};
 use crate::kernel::constants::{
@@ -8,8 +7,8 @@ use crate::kernel::constants::{
 };
 use crate::kernel::mem::PageBuffer;
 use crate::kernel::task::{
-    new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, Signal, SignalAction,
-    SignalMask, ThreadBuilder, UserDescriptor, WaitObject, WaitType,
+    new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, ProgramLoader, Signal,
+    SignalAction, SignalMask, ThreadBuilder, UserDescriptor, WaitObject, WaitType,
 };
 use crate::kernel::user::dataflow::UserString;
 use crate::kernel::user::{UserPointer, UserPointerMut};
@@ -145,19 +144,18 @@ fn execve(exec: *const u8, argv: *const u32, envp: *const u32) -> KResult<()> {
 
     // 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 elf = ParsedElf32::parse(dentry.clone())?;
-    if let Ok((ip, sp, mm_list)) = elf.load(argv, envp) {
+    if let Ok(load_info) = ProgramLoader::parse(dentry.clone())?.load(argv, envp) {
         unsafe {
             // SAFETY: We are doing execve, all other threads are terminated.
-            thread.process.mm_list.replace(Some(mm_list));
+            thread.process.mm_list.replace(Some(load_info.mm_list));
         }
         thread.files.on_exec();
         thread.signal_list.clear_non_ignore();
         thread.set_name(dentry.name().clone());
 
         let mut trap_ctx = thread.trap_ctx.borrow();
-        trap_ctx.set_program_counter(ip.addr());
-        trap_ctx.set_stack_pointer(sp.addr());
+        trap_ctx.set_program_counter(load_info.entry_ip.addr());
+        trap_ctx.set_stack_pointer(load_info.sp.addr());
         Ok(())
     } else {
         // We can't hold any ownership when we call `kill_current`.

+ 2 - 0
src/kernel/task.rs

@@ -1,4 +1,5 @@
 mod kernel_stack;
+mod loader;
 mod process;
 mod process_group;
 mod process_list;
@@ -7,6 +8,7 @@ mod signal;
 mod thread;
 
 pub use kernel_stack::KernelStack;
+pub use loader::ProgramLoader;
 pub use process::{Process, ProcessBuilder, WaitObject, WaitType};
 pub use process_group::ProcessGroup;
 pub use process_list::ProcessList;

+ 119 - 0
src/kernel/task/loader/aux_vec.rs

@@ -0,0 +1,119 @@
+//! Part of code and design forked from asterinas.
+
+// SPDX-License-Identifier: MPL-2.0
+#![expect(dead_code)]
+
+use alloc::collections::BTreeMap;
+
+use crate::prelude::KResult;
+
+/// Auxiliary Vector.
+///
+/// # What is Auxiliary Vector?
+///
+/// Here is a concise description of Auxiliary Vector from GNU's manual:
+///
+///  > When a program is executed, it receives information from the operating system
+///  > about the environment in which it is operating. The form of this information
+///  > is a table of key-value pairs, where the keys are from the set of ‘AT_’
+///  > values in elf.h.
+#[expect(non_camel_case_types)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[repr(u8)]
+pub enum AuxKey {
+    AT_NULL = 0,      /* end of vector */
+    AT_IGNORE = 1,    /* entry should be ignored */
+    AT_EXECFD = 2,    /* file descriptor of program */
+    AT_PHDR = 3,      /* program headers for program */
+    AT_PHENT = 4,     /* size of program header entry */
+    AT_PHNUM = 5,     /* number of program headers */
+    AT_PAGESZ = 6,    /* system page size */
+    AT_BASE = 7,      /* base address of interpreter */
+    AT_FLAGS = 8,     /* flags */
+    AT_ENTRY = 9,     /* entry point of program */
+    AT_NOTELF = 10,   /* program is not ELF */
+    AT_UID = 11,      /* real uid */
+    AT_EUID = 12,     /* effective uid */
+    AT_GID = 13,      /* real gid */
+    AT_EGID = 14,     /* effective gid */
+    AT_PLATFORM = 15, /* string identifying CPU for optimizations */
+    AT_HWCAP = 16,    /* arch dependent hints at CPU capabilities */
+    AT_CLKTCK = 17,   /* frequency at which times() increments */
+
+    /* 18...22 not used */
+    AT_SECURE = 23, /* secure mode boolean */
+    AT_BASE_PLATFORM = 24, /* string identifying real platform, may
+                     * differ from AT_PLATFORM. */
+    AT_RANDOM = 25, /* address of 16 random bytes */
+    AT_HWCAP2 = 26, /* extension of AT_HWCAP */
+
+    /* 28...30 not used */
+    AT_EXECFN = 31, /* filename of program */
+    AT_SYSINFO = 32,
+    AT_SYSINFO_EHDR = 33, /* the start address of the page containing the VDSO */
+}
+
+#[derive(Clone, Default, Debug)]
+pub struct AuxVec<T> {
+    table: BTreeMap<AuxKey, T>,
+}
+
+impl<T> AuxVec<T> {
+    pub const fn new() -> AuxVec<T> {
+        AuxVec {
+            table: BTreeMap::new(),
+        }
+    }
+}
+
+impl AuxVec<u32> {
+    pub fn set(&mut self, key: AuxKey, val: u32) -> KResult<()> {
+        if key == AuxKey::AT_NULL || key == AuxKey::AT_IGNORE {
+            panic!("Set illegal key!");
+        }
+
+        self.table
+            .entry(key)
+            .and_modify(|val_mut: &mut u32| *val_mut = val)
+            .or_insert(val);
+        Ok(())
+    }
+
+    pub fn get(&self, key: AuxKey) -> Option<u32> {
+        self.table.get(&key).copied()
+    }
+
+    pub fn del(&mut self, key: AuxKey) -> Option<u32> {
+        self.table.remove(&key)
+    }
+
+    pub fn table(&self) -> &BTreeMap<AuxKey, u32> {
+        &self.table
+    }
+}
+
+impl AuxVec<u64> {
+    pub fn set(&mut self, key: AuxKey, val: u64) -> KResult<()> {
+        if key == AuxKey::AT_NULL || key == AuxKey::AT_IGNORE {
+            panic!("Set illegal key!");
+        }
+
+        self.table
+            .entry(key)
+            .and_modify(|val_mut: &mut u64| *val_mut = val)
+            .or_insert(val);
+        Ok(())
+    }
+
+    pub fn get(&self, key: AuxKey) -> Option<u64> {
+        self.table.get(&key).copied()
+    }
+
+    pub fn del(&mut self, key: AuxKey) -> Option<u64> {
+        self.table.remove(&key)
+    }
+
+    pub fn table(&self) -> &BTreeMap<AuxKey, u64> {
+        &self.table
+    }
+}

+ 485 - 0
src/kernel/task/loader/elf.rs

@@ -0,0 +1,485 @@
+use super::{LoadInfo, ELF_MAGIC};
+
+use crate::io::UninitBuffer;
+use crate::kernel::task::loader::aux_vec::{AuxKey, AuxVec};
+use crate::path::Path;
+use crate::{
+    io::ByteBuffer,
+    kernel::{
+        constants::ENOEXEC,
+        mem::{FileMapping, MMList, Mapping, Permission},
+        vfs::{dentry::Dentry, FsContext},
+    },
+    prelude::*,
+};
+use align_ext::AlignExt;
+use alloc::vec::Vec;
+use alloc::{ffi::CString, sync::Arc};
+use eonix_mm::{
+    address::{Addr, AddrOps as _, VAddr},
+    paging::PAGE_SIZE,
+};
+
+use xmas_elf::{
+    header::{self, Class, HeaderPt1, Machine_, Type_},
+    program::{self, ProgramHeader32, ProgramHeader64},
+    P32, P64,
+};
+
+const INIT_STACK_SIZE: usize = 0x80_0000;
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct HeaderPt2<P> {
+    pub type_: Type_,
+    pub machine: Machine_,
+    pub version: u32,
+    pub entry_point: P,
+    pub ph_offset: P,
+    pub sh_offset: P,
+    pub flags: u32,
+    pub header_size: u16,
+    pub ph_entry_size: u16,
+    pub ph_count: u16,
+    pub sh_entry_size: u16,
+    pub sh_count: u16,
+    pub sh_str_index: u16,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(C)]
+pub struct ElfHeader<P> {
+    pub pt1: HeaderPt1,
+    pub pt2: HeaderPt2<P>,
+}
+
+pub struct LdsoLoadInfo {
+    pub base: VAddr,
+    pub entry_ip: VAddr,
+}
+
+pub struct Elf32 {
+    pub file: Arc<Dentry>,
+    pub elf_header: ElfHeader<P32>,
+    pub program_headers: Vec<ProgramHeader32>,
+}
+
+impl Elf32 {
+    const DYN_BASE_ADDR: usize = 0x4000_0000;
+    const LDSO_BASE_ADDR: usize = 0xf000_0000;
+    const STACK_BASE_ADDR: usize = 0xffff_0000;
+
+    pub fn parse(elf_file: Arc<Dentry>) -> KResult<Self> {
+        let mut elf_header = UninitBuffer::<ElfHeader<P32>>::new();
+        elf_file.read(&mut elf_header, 0)?;
+
+        let elf_header = elf_header.assume_init().map_err(|_| ENOEXEC)?;
+
+        let ph_offset = elf_header.pt2.ph_offset;
+        let ph_count = elf_header.pt2.ph_count;
+
+        let mut program_headers = vec![ProgramHeader32::default(); ph_count as usize];
+        elf_file.read(
+            &mut ByteBuffer::from(program_headers.as_mut_slice()),
+            ph_offset as usize,
+        )?;
+
+        Ok(Self {
+            file: elf_file,
+            elf_header,
+            program_headers,
+        })
+    }
+
+    fn is_shared_object(&self) -> bool {
+        self.elf_header.pt2.type_.as_type() == header::Type::SharedObject
+    }
+
+    fn entry_point(&self) -> u32 {
+        self.elf_header.pt2.entry_point
+    }
+
+    fn ph_count(&self) -> u16 {
+        self.elf_header.pt2.ph_count
+    }
+
+    fn ph_offset(&self) -> u32 {
+        self.elf_header.pt2.ph_offset
+    }
+
+    fn ph_entry_size(&self) -> u16 {
+        self.elf_header.pt2.ph_entry_size
+    }
+
+    fn ph_addr(&self) -> KResult<u32> {
+        let ph_offset = self.ph_offset();
+        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);
+            }
+        }
+        Err(ENOEXEC)
+    }
+
+    pub fn load(&self, args: Vec<CString>, envs: Vec<CString>) -> KResult<LoadInfo> {
+        let mm_list = MMList::new();
+
+        // Load Segments
+        let (elf_base, data_segment_end) = self.load_segments(&mm_list)?;
+
+        // Load ldso(if have)
+        let ldso_load_info = self.load_ldso(&mm_list)?;
+
+        // Heap
+        mm_list.register_break(data_segment_end + 0x10000);
+
+        let aux_vec = Elf32::init_aux_vec(
+            self,
+            elf_base,
+            ldso_load_info
+                .as_ref()
+                .map(|ldso_load_info| ldso_load_info.base),
+        )?;
+
+        // Map stack
+        let sp = Elf32::create_and_init_stack(&mm_list, args, envs, aux_vec)?;
+
+        let entry_ip = if let Some(ldso_load_info) = ldso_load_info {
+            // Normal shared object(DYN)
+            ldso_load_info.entry_ip.into()
+        } else if self.is_shared_object() {
+            // ldso itself
+            elf_base + self.entry_point() as usize
+        } else {
+            // statically linked executable
+            (self.entry_point() as usize).into()
+        };
+
+        Ok(LoadInfo {
+            entry_ip,
+            sp,
+            mm_list,
+        })
+    }
+
+    fn init_aux_vec(
+        elf: &Elf32,
+        elf_base: VAddr,
+        ldso_base: Option<VAddr>,
+    ) -> KResult<AuxVec<u32>> {
+        let mut aux_vec: AuxVec<u32> = AuxVec::new();
+        let ph_addr = if elf.is_shared_object() {
+            elf_base.addr() as u32 + elf.ph_addr()?
+        } else {
+            elf.ph_addr()?
+        };
+        aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as u32)?;
+        aux_vec.set(AuxKey::AT_PHDR, ph_addr)?;
+        aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u32)?;
+        aux_vec.set(AuxKey::AT_PHENT, elf.ph_entry_size() as u32)?;
+        let elf_entry = if elf.is_shared_object() {
+            elf_base.addr() as u32 + elf.entry_point()
+        } else {
+            elf.entry_point()
+        };
+        aux_vec.set(AuxKey::AT_ENTRY, elf_entry)?;
+
+        if let Some(ldso_base) = ldso_base {
+            aux_vec.set(AuxKey::AT_BASE, ldso_base.addr() as u32)?;
+        }
+        Ok(aux_vec)
+    }
+
+    pub fn load_segments(&self, mm_list: &MMList) -> KResult<(VAddr, VAddr)> {
+        let base: VAddr = if self.is_shared_object() {
+            Elf32::DYN_BASE_ADDR
+        } else {
+            0
+        }
+        .into();
+
+        let mut segments_end = VAddr::NULL;
+
+        for program_header in &self.program_headers {
+            let type_ = program_header.get_type().map_err(|_| ENOEXEC)?;
+
+            if type_ == program::Type::Load {
+                let segment_end = self.load_segment(program_header, mm_list, base)?;
+
+                if segment_end > segments_end {
+                    segments_end = segment_end;
+                }
+            }
+        }
+
+        Ok((base, segments_end))
+    }
+
+    pub fn load_segment(
+        &self,
+        program_header: &ProgramHeader32,
+        mm_list: &MMList,
+        base_addr: VAddr,
+    ) -> KResult<VAddr> {
+        let virtual_addr = base_addr + program_header.virtual_addr as usize;
+        let vmem_vaddr_end = virtual_addr + program_header.mem_size as usize;
+        let load_vaddr_end = virtual_addr + program_header.file_size as usize;
+
+        let vmap_start = virtual_addr.floor();
+        let vmem_len = vmem_vaddr_end.ceil() - vmap_start;
+        let file_len = load_vaddr_end.ceil() - vmap_start;
+        let file_offset = (program_header.offset as usize).align_down(PAGE_SIZE);
+
+        let permission = Permission {
+            read: program_header.flags.is_read(),
+            write: program_header.flags.is_write(),
+            execute: program_header.flags.is_execute(),
+        };
+
+        if file_len != 0 {
+            let real_file_length = load_vaddr_end - vmap_start;
+
+            mm_list.mmap_fixed(
+                vmap_start,
+                file_len,
+                Mapping::File(FileMapping::new(
+                    self.file.clone(),
+                    file_offset,
+                    real_file_length,
+                )),
+                permission,
+            )?;
+        }
+
+        if vmem_len > file_len {
+            mm_list.mmap_fixed(
+                vmap_start + file_len,
+                vmem_len - file_len,
+                Mapping::Anonymous,
+                permission,
+            )?;
+        }
+
+        Ok(vmap_start + vmem_len)
+    }
+
+    pub fn load_ldso(&self, mm_list: &MMList) -> KResult<Option<LdsoLoadInfo>> {
+        let ldso_path = self.ldso_path()?;
+
+        if let Some(ldso_path) = ldso_path {
+            let fs_context = FsContext::global();
+            let ldso_file =
+                Dentry::open(fs_context, Path::new(ldso_path.as_bytes()).unwrap(), true).unwrap();
+            let ldso_elf = Elf32::parse(ldso_file).unwrap();
+
+            let base = VAddr::from(Elf32::LDSO_BASE_ADDR);
+
+            for program_header in &ldso_elf.program_headers {
+                let type_ = program_header.get_type().map_err(|_| ENOEXEC)?;
+
+                if type_ == program::Type::Load {
+                    ldso_elf.load_segment(program_header, mm_list, base)?;
+                }
+            }
+
+            return Ok(Some(LdsoLoadInfo {
+                base,
+                entry_ip: base + ldso_elf.entry_point() as usize,
+            }));
+        }
+
+        Ok(None)
+    }
+
+    fn ldso_path(&self) -> KResult<Option<String>> {
+        for program_header in &self.program_headers {
+            let type_ = program_header.get_type().map_err(|_| ENOEXEC)?;
+
+            if type_ == program::Type::Interp {
+                let file_size = program_header.file_size as usize;
+                let file_offset = program_header.offset as usize;
+
+                let mut ldso_vec = vec![0u8; file_size - 1]; // -1 due to '\0'
+                self.file
+                    .read(&mut ByteBuffer::from(ldso_vec.as_mut_slice()), file_offset)?;
+                let ldso_path = String::from_utf8(ldso_vec).map_err(|_| ENOEXEC)?;
+                return Ok(Some(ldso_path));
+            }
+        }
+        Ok(None)
+    }
+
+    fn create_and_init_stack(
+        mm_list: &MMList,
+        args: Vec<CString>,
+        envs: Vec<CString>,
+        aux_vec: AuxVec<u32>,
+    ) -> KResult<VAddr> {
+        mm_list.mmap_fixed(
+            VAddr::from(Elf32::STACK_BASE_ADDR - INIT_STACK_SIZE),
+            INIT_STACK_SIZE,
+            Mapping::Anonymous,
+            Permission {
+                read: true,
+                write: true,
+                execute: false,
+            },
+        )?;
+
+        let mut sp = VAddr::from(Elf32::STACK_BASE_ADDR);
+
+        let env_pointers = Elf32::push_strings(&mm_list, &mut sp, envs)?;
+        let arg_pointers = Elf32::push_strings(&mm_list, &mut sp, args)?;
+
+        let argc = arg_pointers.len() as u32;
+
+        Elf32::stack_alignment(&mut sp, &arg_pointers, &env_pointers, &aux_vec);
+        Elf32::push_aux_vec(&mm_list, &mut sp, aux_vec)?;
+        Elf32::push_pointers(&mm_list, &mut sp, env_pointers)?;
+        Elf32::push_pointers(&mm_list, &mut sp, arg_pointers)?;
+        Elf32::push_u32(&mm_list, &mut sp, argc)?;
+
+        assert_eq!(sp.floor_to(16), sp);
+        Ok(sp)
+    }
+
+    fn stack_alignment(
+        sp: &mut VAddr,
+        arg_pointers: &Vec<u32>,
+        env_pointers: &Vec<u32>,
+        aux_vec: &AuxVec<u32>,
+    ) {
+        let aux_vec_size = (aux_vec.table().len() + 1) * (size_of::<u32>() * 2);
+        let envp_pointers_size = (env_pointers.len() + 1) * size_of::<u32>();
+        let argv_pointers_size = (arg_pointers.len() + 1) * size_of::<u32>();
+        let argc_size = size_of::<u32>();
+        let all_size = aux_vec_size + envp_pointers_size + argv_pointers_size + argc_size;
+
+        let align_sp = (sp.addr() - all_size).align_down(16);
+        *sp = VAddr::from(align_sp + all_size);
+    }
+
+    fn push_strings(mm_list: &MMList, sp: &mut VAddr, strings: Vec<CString>) -> KResult<Vec<u32>> {
+        let mut addrs = Vec::with_capacity(strings.len());
+        for string in strings.iter().rev() {
+            let len = string.as_bytes_with_nul().len();
+            *sp = *sp - len;
+            mm_list.access_mut(*sp, len, |offset, data| {
+                data.copy_from_slice(&string.as_bytes_with_nul()[offset..offset + data.len()])
+            })?;
+            addrs.push(sp.addr() as u32);
+        }
+        addrs.reverse();
+        Ok(addrs)
+    }
+
+    fn push_pointers(mm_list: &MMList, sp: &mut VAddr, mut pointers: Vec<u32>) -> KResult<()> {
+        pointers.push(0);
+        *sp = *sp - pointers.len() * size_of::<u32>();
+
+        mm_list.access_mut(*sp, pointers.len() * size_of::<u32>(), |offset, data| {
+            data.copy_from_slice(unsafe {
+                core::slice::from_raw_parts(
+                    pointers.as_ptr().byte_add(offset) as *const u8,
+                    data.len(),
+                )
+            })
+        })?;
+
+        Ok(())
+    }
+
+    fn push_u32(mm_list: &MMList, sp: &mut VAddr, val: u32) -> KResult<()> {
+        *sp = *sp - size_of::<u32>();
+
+        mm_list.access_mut(*sp, size_of::<u32>(), |_, data| {
+            data.copy_from_slice(unsafe {
+                core::slice::from_raw_parts(&val as *const _ as *const u8, data.len())
+            })
+        })?;
+
+        Ok(())
+    }
+
+    fn push_aux_vec(mm_list: &MMList, sp: &mut VAddr, aux_vec: AuxVec<u32>) -> KResult<()> {
+        let mut longs: Vec<u32> = vec![];
+
+        // Write Auxiliary vectors
+        let aux_vec: Vec<_> = aux_vec
+            .table()
+            .iter()
+            .map(|(aux_key, aux_value)| (*aux_key, *aux_value))
+            .collect();
+
+        for (aux_key, aux_value) in aux_vec.iter() {
+            longs.push(*aux_key as u32);
+            longs.push(*aux_value);
+        }
+
+        // Write NULL auxiliary
+        longs.push(AuxKey::AT_NULL as u32);
+        longs.push(0);
+
+        *sp = *sp - longs.len() * size_of::<u32>();
+
+        mm_list.access_mut(*sp, longs.len() * size_of::<u32>(), |offset, data| {
+            data.copy_from_slice(unsafe {
+                core::slice::from_raw_parts(
+                    longs.as_ptr().byte_add(offset) as *const u8,
+                    data.len(),
+                )
+            })
+        })?;
+
+        Ok(())
+    }
+}
+
+pub struct Elf64 {
+    elf_header: ElfHeader<P64>,
+    program_headers: Vec<ProgramHeader64>,
+}
+
+impl Elf64 {
+    // const LDSO_BASE_ADDR: usize = 0xffff00000000;
+    // const STACK_BASE_ADDR: usize = 0xffff_ff00_0000;
+    // const DYN_BASE_ADDR: usize = 0xaaaa00000000;
+
+    fn parse(file: Arc<Dentry>) -> KResult<Self> {
+        todo!()
+    }
+
+    fn load(&self, args: Vec<CString>, envs: Vec<CString>) -> KResult<LoadInfo> {
+        todo!()
+    }
+}
+
+pub enum Elf {
+    ELF32(Elf32),
+    ELF64(Elf64),
+}
+
+impl Elf {
+    pub fn parse(elf_file: Arc<Dentry>) -> KResult<Self> {
+        let mut header_pt1 = UninitBuffer::<HeaderPt1>::new();
+        elf_file.read(&mut header_pt1, 0)?;
+
+        let header_pt1 = header_pt1.assume_init().map_err(|_| ENOEXEC)?;
+        assert_eq!(header_pt1.magic, ELF_MAGIC);
+
+        match header_pt1.class() {
+            Class::ThirtyTwo => Ok(Elf::ELF32(Elf32::parse(elf_file)?)),
+            Class::SixtyFour => Ok(Elf::ELF64(Elf64::parse(elf_file)?)),
+            _ => Err(ENOEXEC),
+        }
+    }
+
+    pub fn load(&self, args: Vec<CString>, envs: Vec<CString>) -> KResult<LoadInfo> {
+        match &self {
+            Elf::ELF32(elf32) => elf32.load(args, envs),
+            Elf::ELF64(elf64) => elf64.load(args, envs),
+        }
+    }
+}

+ 53 - 0
src/kernel/task/loader/mod.rs

@@ -0,0 +1,53 @@
+use crate::io::ByteBuffer;
+use crate::kernel::constants::ENOEXEC;
+use crate::kernel::task::loader::elf::Elf;
+use crate::{
+    kernel::{mem::MMList, vfs::dentry::Dentry},
+    prelude::*,
+};
+
+use alloc::{ffi::CString, sync::Arc};
+use eonix_mm::address::VAddr;
+
+mod aux_vec;
+mod elf;
+
+const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
+
+#[derive(Debug)]
+pub struct LoadInfo {
+    pub entry_ip: VAddr,
+    pub sp: VAddr,
+    pub mm_list: MMList,
+}
+
+enum Object {
+    ELF(Elf),
+}
+
+pub struct ProgramLoader {
+    object: Object,
+}
+
+impl ProgramLoader {
+    pub fn parse(file: Arc<Dentry>) -> KResult<Self> {
+        let mut magic = [0u8; 4];
+        let mut buffer = ByteBuffer::new(magic.as_mut_slice());
+        file.read(&mut buffer, 0)?;
+
+        let object = match magic {
+            ELF_MAGIC => Elf::parse(file),
+            _ => Err(ENOEXEC),
+        }?;
+
+        Ok(ProgramLoader {
+            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),
+        }
+    }
+}

+ 10 - 8
src/lib.rs

@@ -9,7 +9,6 @@
 extern crate alloc;
 
 mod driver;
-mod elf;
 mod fs;
 mod hash;
 mod io;
@@ -26,14 +25,15 @@ use core::{
     hint::spin_loop,
     sync::atomic::{AtomicBool, Ordering},
 };
-use elf::ParsedElf32;
 use eonix_hal::{processor::CPU, traits::trap::IrqState, trap::disable_irqs_save};
 use eonix_mm::address::PRange;
 use eonix_runtime::{run::FutureRun, scheduler::Scheduler, task::Task};
 use kernel::{
     mem::GlobalPageAlloc,
     pcie::init_pcie,
-    task::{new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, ThreadBuilder},
+    task::{
+        new_thread_runnable, KernelStack, ProcessBuilder, ProcessList, ProgramLoader, ThreadBuilder,
+    },
     vfs::{
         dentry::Dentry,
         mount::{do_mount, MS_NOATIME, MS_NODEV, MS_NOSUID, MS_RDONLY},
@@ -130,7 +130,7 @@ async fn init_process(early_kstack: PRange) {
     fs::procfs::init();
     fs::fat32::init();
 
-    let (ip, sp, mm_list) = {
+    let load_info = {
         // mount fat32 /mnt directory
         let fs_context = FsContext::global();
         let mnt_dir = Dentry::open(fs_context, Path::new(b"/mnt/").unwrap(), true).unwrap();
@@ -162,17 +162,19 @@ async fn init_process(early_kstack: PRange) {
             CString::new("PWD=/").unwrap(),
         ];
 
-        let elf = ParsedElf32::parse(init.clone()).unwrap();
-        elf.load(argv, envp).unwrap()
+        ProgramLoader::parse(init.clone())
+            .unwrap()
+            .load(argv, envp)
+            .unwrap()
     };
 
     let thread_builder = ThreadBuilder::new()
         .name(Arc::from(&b"busybox"[..]))
-        .entry(ip, sp);
+        .entry(load_info.entry_ip, load_info.sp);
 
     let mut process_list = Task::block_on(ProcessList::get().write());
     let (thread, process) = ProcessBuilder::new()
-        .mm_list(mm_list)
+        .mm_list(load_info.mm_list)
         .thread_builder(thread_builder)
         .build(&mut process_list);
 

二進制
user-programs/dynamic_test


二進制
user-programs/ld-musl-i386.so.1