asterinas/kernel/src/process/program_loader/mod.rs

117 lines
3.8 KiB
Rust
Raw Normal View History

2024-01-03 03:22:36 +00:00
// SPDX-License-Identifier: MPL-2.0
2023-03-14 07:35:38 +00:00
pub mod elf;
mod shebang;
use self::{
2025-10-17 09:55:41 +00:00
elf::{load_elf_to_vmar, ElfHeaders, ElfLoadInfo},
shebang::parse_shebang_line,
};
use crate::{
fs::{
2025-10-16 15:57:17 +00:00
fs_resolver::{FsPath, FsResolver},
utils::{Inode, InodeType, Permission},
},
prelude::*,
2025-10-17 09:55:41 +00:00
vm::vmar::Vmar,
};
2025-04-03 06:16:17 +00:00
/// Represents an executable file that is ready to be loaded into memory and executed.
///
/// This struct encapsulates the ELF file to be executed along with its header data,
/// the `argv` and the `envp` which is required for the program execution.
pub struct ProgramToLoad {
elf_inode: Arc<dyn Inode>,
elf_headers: ElfHeaders,
2023-03-14 07:35:38 +00:00
argv: Vec<CString>,
envp: Vec<CString>,
2025-04-03 06:16:17 +00:00
}
impl ProgramToLoad {
/// Constructs a new `ProgramToLoad` from a file, handling shebang interpretation if needed.
///
/// 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,
2025-10-17 09:55:41 +00:00
/// then it will trigger recursion. We will try to setup VMAR for the interpreter.
2025-04-03 06:16:17 +00:00
/// 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 build_from_inode(
elf_inode: &Arc<dyn Inode>,
2025-04-03 06:16:17 +00:00
fs_resolver: &FsResolver,
argv: Vec<CString>,
envp: Vec<CString>,
recursion_limit: usize,
) -> Result<Self> {
let file_first_page = {
// Read the first page of file header, which must contain the ELF header.
let mut buffer = Box::new([0u8; PAGE_SIZE]);
elf_inode.read_bytes_at(0, &mut *buffer)?;
buffer
2023-06-13 02:13:00 +00:00
};
if let Some(mut new_argv) = parse_shebang_line(&*file_first_page)? {
2025-04-03 06:16:17 +00:00
if recursion_limit == 0 {
return_errno_with_message!(Errno::ELOOP, "the recursieve limit is reached");
}
new_argv.extend_from_slice(&argv);
let interpreter = {
let filename = new_argv[0].to_str()?.to_string();
2025-10-16 15:57:17 +00:00
let fs_path = FsPath::try_from(filename.as_str())?;
fs_resolver.lookup_inode(&fs_path)?
2025-04-03 06:16:17 +00:00
};
check_executable_inode(interpreter.inode())?;
return Self::build_from_inode(
interpreter.inode(),
2025-04-03 06:16:17 +00:00
fs_resolver,
new_argv,
envp,
recursion_limit - 1,
);
}
let elf_headers = ElfHeaders::parse_elf(&*file_first_page)?;
2025-04-03 06:16:17 +00:00
Ok(Self {
elf_inode: elf_inode.clone(),
elf_headers,
2025-04-03 06:16:17 +00:00
argv,
2023-03-14 07:35:38 +00:00
envp,
2025-04-03 06:16:17 +00:00
})
2023-03-14 07:35:38 +00:00
}
2025-04-03 06:16:17 +00:00
/// Loads the executable into the specified virtual memory space.
///
/// Returns the information about the ELF loading process.
2025-10-24 02:04:38 +00:00
pub fn load_to_vmar(self, vmar: &Vmar, fs_resolver: &FsResolver) -> Result<ElfLoadInfo> {
2025-10-17 09:55:41 +00:00
let elf_load_info = load_elf_to_vmar(
vmar,
&self.elf_inode,
2025-04-03 06:16:17 +00:00
fs_resolver,
self.elf_headers,
2025-04-03 06:16:17 +00:00
self.argv,
self.envp,
)?;
2023-12-06 07:03:52 +00:00
2025-09-25 11:40:52 +00:00
Ok(elf_load_info)
2025-04-03 06:16:17 +00:00
}
2023-03-14 07:35:38 +00:00
}
pub fn check_executable_inode(inode: &Arc<dyn Inode>) -> Result<()> {
if inode.type_().is_directory() {
return_errno_with_message!(Errno::EISDIR, "the inode is a directory");
}
if inode.type_() == InodeType::SymLink {
return_errno_with_message!(Errno::ELOOP, "the inode is a symbolic link");
2024-12-26 13:35:56 +00:00
}
if !inode.type_().is_regular_file() {
return_errno_with_message!(Errno::EACCES, "the inode is not a regular file");
}
if inode.check_permission(Permission::MAY_EXEC).is_err() {
return_errno_with_message!(Errno::EACCES, "the inode is not executable");
}
Ok(())
}