2024-01-03 03:22:36 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2025-12-13 13:20:56 +00:00
|
|
|
pub(super) mod elf;
|
2023-03-14 07:35:38 +00:00
|
|
|
mod shebang;
|
|
|
|
|
|
2024-02-25 14:09:24 +00:00
|
|
|
use self::{
|
2025-12-08 12:53:18 +00:00
|
|
|
elf::{ElfHeaders, ElfLoadInfo, load_elf_to_vmar},
|
2024-02-25 14:09:24 +00:00
|
|
|
shebang::parse_shebang_line,
|
|
|
|
|
};
|
|
|
|
|
use crate::{
|
|
|
|
|
fs::{
|
2025-10-16 15:57:17 +00:00
|
|
|
fs_resolver::{FsPath, FsResolver},
|
2025-10-22 16:21:14 +00:00
|
|
|
utils::{Inode, InodeType, Permission},
|
2024-02-25 14:09:24 +00:00
|
|
|
},
|
|
|
|
|
prelude::*,
|
2025-10-17 09:55:41 +00:00
|
|
|
vm::vmar::Vmar,
|
2024-02-25 14:09:24 +00:00
|
|
|
};
|
2023-09-01 02:25:37 +00:00
|
|
|
|
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.
|
2025-12-13 13:20:56 +00:00
|
|
|
pub(super) struct ProgramToLoad {
|
2025-10-22 16:21:14 +00:00
|
|
|
elf_inode: Arc<dyn Inode>,
|
2025-12-03 11:42:45 +00:00
|
|
|
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 {
|
2025-12-13 13:20:56 +00:00
|
|
|
/// Constructs a new `ProgramToLoad` from an inode and handles shebang interpretation if
|
|
|
|
|
/// necessary.
|
|
|
|
|
pub(super) fn build_from_inode(
|
|
|
|
|
mut elf_inode: Arc<dyn Inode>,
|
2025-04-03 06:16:17 +00:00
|
|
|
fs_resolver: &FsResolver,
|
2025-12-13 13:20:56 +00:00
|
|
|
mut argv: Vec<CString>,
|
2025-04-03 06:16:17 +00:00
|
|
|
envp: Vec<CString>,
|
|
|
|
|
) -> Result<Self> {
|
2025-12-13 13:20:56 +00:00
|
|
|
check_executable_inode(elf_inode.as_ref())?;
|
|
|
|
|
|
|
|
|
|
// A limit to the recursion depth of shebang executables.
|
|
|
|
|
//
|
|
|
|
|
// If the interpreter is a shebang, then recursion will be triggered. If it loops, we
|
|
|
|
|
// should fail. We follow the same limit as Linux.
|
|
|
|
|
let mut recursive_limit = 5;
|
|
|
|
|
|
|
|
|
|
let (file_first_page, len) = loop {
|
|
|
|
|
// Read the first page of the file, which should contain a shebang or an ELF header.
|
|
|
|
|
let (file_first_page, len) = {
|
|
|
|
|
let mut buffer = Box::new([0u8; PAGE_SIZE]);
|
|
|
|
|
let len = elf_inode.read_bytes_at(0, &mut *buffer)?;
|
|
|
|
|
(buffer, len)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let Some(mut new_argv) = parse_shebang_line(&file_first_page[..len])? else {
|
|
|
|
|
break (file_first_page, len);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if recursive_limit == 0 {
|
2025-04-03 06:16:17 +00:00
|
|
|
return_errno_with_message!(Errno::ELOOP, "the recursieve limit is reached");
|
|
|
|
|
}
|
2025-12-13 13:20:56 +00:00
|
|
|
recursive_limit -= 1;
|
|
|
|
|
|
2025-04-03 06:16:17 +00:00
|
|
|
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())?;
|
2025-12-26 12:19:53 +00:00
|
|
|
fs_resolver.lookup(&fs_path)?
|
2025-04-03 06:16:17 +00:00
|
|
|
};
|
2025-12-13 13:20:56 +00:00
|
|
|
check_executable_inode(interpreter.inode().as_ref())?;
|
|
|
|
|
|
|
|
|
|
// Update the argument list and the executable inode. Then, try again.
|
|
|
|
|
new_argv.extend(argv);
|
|
|
|
|
argv = new_argv;
|
|
|
|
|
elf_inode = interpreter.inode().clone();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let elf_headers = ElfHeaders::parse(&file_first_page[..len])?;
|
2025-12-03 11:42:45 +00:00
|
|
|
|
2025-04-03 06:16:17 +00:00
|
|
|
Ok(Self {
|
2025-12-13 13:20:56 +00:00
|
|
|
elf_inode,
|
2025-12-03 11:42:45 +00:00
|
|
|
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
|
|
|
}
|
2024-03-26 07:54:08 +00:00
|
|
|
|
2025-04-03 06:16:17 +00:00
|
|
|
/// Loads the executable into the specified virtual memory space.
|
|
|
|
|
///
|
2025-10-22 16:21:14 +00:00
|
|
|
/// Returns the information about the ELF loading process.
|
2025-12-13 13:20:56 +00:00
|
|
|
pub(super) 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,
|
2025-10-22 16:21:14 +00:00
|
|
|
&self.elf_inode,
|
2025-04-03 06:16:17 +00:00
|
|
|
fs_resolver,
|
2025-12-03 11:42:45 +00:00
|
|
|
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
|
|
|
}
|
2023-08-16 09:39:27 +00:00
|
|
|
|
2025-12-13 13:20:56 +00:00
|
|
|
fn check_executable_inode(inode: &dyn Inode) -> Result<()> {
|
2025-10-22 16:21:14 +00:00
|
|
|
if inode.type_().is_directory() {
|
|
|
|
|
return_errno_with_message!(Errno::EISDIR, "the inode is a directory");
|
2023-08-16 09:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-22 16:21:14 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2025-10-22 16:21:14 +00:00
|
|
|
if !inode.type_().is_regular_file() {
|
|
|
|
|
return_errno_with_message!(Errno::EACCES, "the inode is not a regular file");
|
2023-08-16 09:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
2025-10-22 16:21:14 +00:00
|
|
|
if inode.check_permission(Permission::MAY_EXEC).is_err() {
|
|
|
|
|
return_errno_with_message!(Errno::EACCES, "the inode is not executable");
|
2023-08-16 09:39:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|