load elf from file

This commit is contained in:
Jianfeng Jiang
2023-03-07 17:13:35 +08:00
committed by Tate, Hongliang Tian
parent 57297e0cd5
commit 896910b44a
19 changed files with 265 additions and 458 deletions

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:c04094e4c0da36e9f8cc5059f540dbc2123d5e4caa1d0b7427577ee734710b23 oid sha256:c7cc90df87ade7ff2cb494e13678aceaf93542883558fda947bd2fc01e2d73a6
size 871952 size 871952

View File

@ -4,10 +4,11 @@
int main() { int main() {
char* argv[] = { "argv1", "argv2", NULL }; char* argv[] = { "argv1", "argv2", NULL };
char* envp[] = { "home=/", "version=1.1", NULL }; char* envp[] = { "home=/", "version=1.1", NULL };
printf("Execve a new file ./hello:\n"); // The hello will be put at /execve/hello in InitRamfs
printf("Execve a new file /execve/hello:\n");
// flush the stdout content to ensure the content print to console // flush the stdout content to ensure the content print to console
fflush(stdout); fflush(stdout);
execve("./hello", argv, envp); execve("/execve/hello", argv, envp);
printf("Should not print\n"); printf("Should not print\n");
fflush(stdout); fflush(stdout);
return 0; return 0;

View File

@ -6,7 +6,6 @@ fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
} }
fn limine_build_script() -> Result<(), Box<dyn Error + Send + Sync>> { fn limine_build_script() -> Result<(), Box<dyn Error + Send + Sync>> {
// Have cargo rerun this script if the linker script or CARGO_PKG_ENV changes. // Have cargo rerun this script if the linker script or CARGO_PKG_ENV changes.
println!("cargo:rerun-if-changed=boot/limine/conf/linker.ld"); println!("cargo:rerun-if-changed=boot/limine/conf/linker.ld");
println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME"); println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME");

View File

@ -68,17 +68,30 @@ pub fn init_thread() {
thread.tid() thread.tid()
); );
// FIXME: should be running this apps before we running shell?
println!("");
println!("[kernel] Running test programs");
println!("");
// Run test apps
for app in get_all_apps().unwrap().into_iter() {
let UserApp {
executable_path: app_name,
argv,
envp,
} = app;
println!("[jinux-std/lib.rs] spwan {:?} process", app_name);
Process::spawn_user_process(app_name.clone(), argv, Vec::new());
}
// Run busybox ash // Run busybox ash
let UserApp { let UserApp {
elf_path: app_name, executable_path: app_name,
app_content,
argv, argv,
envp, envp,
} = get_busybox_app().unwrap(); } = get_busybox_app().unwrap();
let app_content = app_content.into_boxed_slice();
println!(""); println!("");
println!("BusyBox v1.35.0 built-in shell (ash)\n"); println!("BusyBox v1.35.0 built-in shell (ash)\n");
Process::spawn_user_process(app_name.clone(), Box::leak(app_content), argv, Vec::new()); Process::spawn_user_process(app_name.clone(), argv, Vec::new());
loop { loop {
// We don't have preemptive scheduler now. // We don't have preemptive scheduler now.

View File

@ -6,6 +6,8 @@ pub(crate) use alloc::collections::BTreeSet;
pub(crate) use alloc::collections::LinkedList; pub(crate) use alloc::collections::LinkedList;
pub(crate) use alloc::collections::VecDeque; pub(crate) use alloc::collections::VecDeque;
pub(crate) use alloc::ffi::CString; pub(crate) use alloc::ffi::CString;
pub(crate) use alloc::string::String;
pub(crate) use alloc::string::ToString;
pub(crate) use alloc::sync::Arc; pub(crate) use alloc::sync::Arc;
pub(crate) use alloc::sync::Weak; pub(crate) use alloc::sync::Weak;
pub(crate) use alloc::vec; pub(crate) use alloc::vec;

View File

@ -218,8 +218,8 @@ fn clone_child_process(parent_context: CpuContext, clone_args: CloneArgs) -> Res
// clone system V semaphore // clone system V semaphore
clone_sysvsem(clone_flags)?; clone_sysvsem(clone_flags)?;
let child_elf_path = current.filename().unwrap().clone(); let child_elf_path = current.executable_path().unwrap().clone();
let child_thread_name = ThreadName::new_from_elf_path(&child_elf_path)?; let child_thread_name = ThreadName::new_from_executable_path(&child_elf_path)?;
// inherit parent's sig mask // inherit parent's sig mask
let current_thread = current_thread!(); let current_thread = current_thread!();

View File

@ -45,6 +45,42 @@ impl Elf {
program_headers, program_headers,
}) })
} }
// The following info is used to setup init stack
/// the entry point of the elf
pub fn entry_point(&self) -> Vaddr {
self.elf_header.pt2.entry_point as Vaddr
}
/// page header table offset
pub fn ph_off(&self) -> u64 {
self.elf_header.pt2.ph_offset
}
/// number of program headers
pub fn ph_count(&self) -> u16 {
self.elf_header.pt2.ph_count
}
/// The size of a program header
pub fn ph_ent(&self) -> u16 {
self.elf_header.pt2.ph_entry_size
}
/// The virtual addr of program headers table address
pub fn ph_addr(&self) -> Result<Vaddr> {
let ph_offset = self.ph_off();
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) as Vaddr,
);
}
}
return_errno_with_message!(
Errno::ENOEXEC,
"can not find program header table address in elf"
);
}
} }
pub struct ElfHeader { pub struct ElfHeader {

View File

@ -1,36 +1,47 @@
use core::ops::Range;
use crate::fs::file_handle::FileHandle;
use crate::fs::utils::SeekFrom;
use crate::prelude::*; use crate::prelude::*;
use crate::vm::vmar::{get_intersected_range, is_intersected}; use crate::vm::vmar::{get_intersected_range, is_intersected};
use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo}; use jinux_frame::vm::{VmAllocOptions, VmFrameVec, VmIo};
use jinux_frame::AlignExt; use jinux_frame::AlignExt;
use xmas_elf::program::ProgramHeader64;
use crate::vm::vmo::Pager; use crate::vm::vmo::Pager;
use super::load_elf::ElfSegment; // use super::load_elf::ElfSegment;
/// The pager behind a elf segment /// The pager behind a elf segment
pub struct ElfSegmentPager { pub struct ElfSegmentPager {
/// The pager size /// The pager size
pager_size: usize, pager_size: usize,
/// data for current segment /// the back up file
segment_data: &'static [u8], file: Arc<FileHandle>,
/// The segment offset in backup file
file_offset: usize,
/// The segment size in backup file
file_size: usize,
/// The offset for the segment data. /// The offset for the segment data.
/// The pager always starts at page-align address, while the segment data may start at any address. /// The pager always starts at page-align address, while the segment data may start at any address.
/// So the offset will be the segment data start address % PAGE_SIZE /// So the offset will be the segment data start address % PAGE_SIZE
offset: usize, page_offset: usize,
} }
impl ElfSegmentPager { impl ElfSegmentPager {
pub fn new(elf_file_content: &'static [u8], elf_segment: &ElfSegment) -> Self { pub fn new(file: Arc<FileHandle>, program_header: &ProgramHeader64) -> Self {
let start = elf_segment.start_address().align_down(PAGE_SIZE); let ph_start = program_header.virtual_addr as Vaddr;
let end = elf_segment.end_address().align_up(PAGE_SIZE); let ph_end = ph_start + program_header.mem_size as Vaddr;
let start = ph_start.align_down(PAGE_SIZE);
let end = ph_end.align_up(PAGE_SIZE);
let pager_size = end - start; let pager_size = end - start;
let offset = elf_segment.start_address() % PAGE_SIZE; let offset = ph_start % PAGE_SIZE;
let elf_file_segment =
&elf_file_content[elf_segment.offset..elf_segment.offset + elf_segment.file_size];
Self { Self {
pager_size, pager_size,
segment_data: elf_file_segment, file,
offset, file_offset: program_header.offset as usize,
file_size: program_header.file_size as usize,
page_offset: offset,
} }
} }
} }
@ -40,21 +51,28 @@ impl Pager for ElfSegmentPager {
if offset >= self.pager_size { if offset >= self.pager_size {
return_errno_with_message!(Errno::EINVAL, "offset exceeds pager size"); return_errno_with_message!(Errno::EINVAL, "offset exceeds pager size");
} }
let vm_alloc_option = VmAllocOptions::new(1); let vm_alloc_option = VmAllocOptions::new(1);
let mut vm_frames = VmFrameVec::allocate(&vm_alloc_option)?; let mut vm_frames = VmFrameVec::allocate(&vm_alloc_option)?;
vm_frames.zero(); vm_frames.zero();
let page_start = offset.align_down(PAGE_SIZE); let page_start = offset.align_down(PAGE_SIZE);
let page_end = page_start + PAGE_SIZE; let page_end = page_start + PAGE_SIZE;
let page_range = page_start..page_end; let page_range = page_start..page_end;
let data_range = self.offset..self.offset + self.segment_data.len(); let segment_range = self.page_offset..self.page_offset + self.file_size;
if is_intersected(&page_range, &data_range) { if is_intersected(&page_range, &segment_range) {
let intersected_range = get_intersected_range(&page_range, &data_range); let intersected_range = get_intersected_range(&page_range, &segment_range);
let data_write_range = let segment_from_file_range = (intersected_range.start - self.page_offset)
(intersected_range.start - self.offset)..(intersected_range.end - self.offset); ..(intersected_range.end - self.page_offset);
let write_content = &self.segment_data[data_write_range]; let mut segment_data = vec![0u8; segment_from_file_range.len()];
self.file.seek(SeekFrom::Start(
self.file_offset + segment_from_file_range.start,
))?;
self.file.read(&mut segment_data)?;
let write_offset = intersected_range.start % PAGE_SIZE; let write_offset = intersected_range.start % PAGE_SIZE;
vm_frames.write_bytes(write_offset, write_content)?; vm_frames.write_bytes(write_offset, &segment_data)?;
} }
let vm_frame = vm_frames.pop().unwrap(); let vm_frame = vm_frames.pop().unwrap();
Ok(vm_frame) Ok(vm_frame)
} }

View File

@ -16,7 +16,7 @@ use jinux_frame::{
}; };
use super::aux_vec::{AuxKey, AuxVec}; use super::aux_vec::{AuxKey, AuxVec};
use super::load_elf::ElfHeaderInfo; use super::elf_file::Elf;
pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000; pub const INIT_STACK_BASE: Vaddr = 0x0000_0000_2000_0000;
pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB pub const INIT_STACK_SIZE: usize = 0x1000 * 16; // 64KB
@ -79,7 +79,6 @@ pub struct InitStack {
impl InitStack { impl InitStack {
/// initialize user stack on base addr /// initialize user stack on base addr
pub fn new( pub fn new(
// filename: CString,
init_stack_top: Vaddr, init_stack_top: Vaddr,
init_stack_size: usize, init_stack_size: usize,
argv: Vec<CString>, argv: Vec<CString>,
@ -115,14 +114,9 @@ impl InitStack {
self.init_stack_top - self.init_stack_size self.init_stack_top - self.init_stack_size
} }
pub fn init( pub fn init(&mut self, root_vmar: &Vmar<Full>, elf: &Elf) -> Result<()> {
&mut self,
root_vmar: &Vmar<Full>,
elf_header_info: &ElfHeaderInfo,
ph_addr: Vaddr,
) -> Result<()> {
self.map_and_zeroed(root_vmar)?; self.map_and_zeroed(root_vmar)?;
self.write_stack_content(root_vmar, elf_header_info, ph_addr)?; self.write_stack_content(root_vmar, elf)?;
self.debug_print_stack_content(root_vmar); self.debug_print_stack_content(root_vmar);
Ok(()) Ok(())
} }
@ -161,12 +155,7 @@ impl InitStack {
Ok(()) Ok(())
} }
fn write_stack_content( fn write_stack_content(&mut self, root_vmar: &Vmar<Full>, elf: &Elf) -> Result<()> {
&mut self,
root_vmar: &Vmar<Full>,
elf_header_info: &ElfHeaderInfo,
ph_addr: Vaddr,
) -> Result<()> {
// write envp string // write envp string
let envp_pointers = self.write_envp_strings(root_vmar)?; let envp_pointers = self.write_envp_strings(root_vmar)?;
// write argv string // write argv string
@ -176,11 +165,9 @@ impl InitStack {
let random_value_pointer = self.write_bytes(&random_value, root_vmar)?; let random_value_pointer = self.write_bytes(&random_value, root_vmar)?;
self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?; self.aux_vec.set(AuxKey::AT_RANDOM, random_value_pointer)?;
self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?; self.aux_vec.set(AuxKey::AT_PAGESZ, PAGE_SIZE as _)?;
self.aux_vec.set(AuxKey::AT_PHDR, ph_addr as u64)?; self.aux_vec.set(AuxKey::AT_PHDR, elf.ph_addr()? as u64)?;
self.aux_vec self.aux_vec.set(AuxKey::AT_PHNUM, elf.ph_count() as u64)?;
.set(AuxKey::AT_PHNUM, elf_header_info.ph_num as u64)?; self.aux_vec.set(AuxKey::AT_PHENT, elf.ph_ent() as u64)?;
self.aux_vec
.set(AuxKey::AT_PHENT, elf_header_info.ph_ent as u64)?;
self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers)?; self.adjust_stack_alignment(root_vmar, &envp_pointers, &argv_pointers)?;
self.write_aux_vec(root_vmar)?; self.write_aux_vec(root_vmar)?;
self.write_envp_pointers(root_vmar, envp_pointers)?; self.write_envp_pointers(root_vmar, envp_pointers)?;

View File

@ -1,6 +1,8 @@
//! This module is used to parse elf file content to get elf_load_info. //! This module is used to parse elf file content to get elf_load_info.
//! When create a process from elf file, we will use the elf_load_info to construct the VmSpace //! When create a process from elf file, we will use the elf_load_info to construct the VmSpace
use crate::fs::file_handle::FileHandle;
use crate::process::elf::init_stack::InitStack;
use crate::vm::perms::VmPerms; use crate::vm::perms::VmPerms;
use crate::vm::vmo::VmoRightsOp; use crate::vm::vmo::VmoRightsOp;
use crate::{ use crate::{
@ -17,234 +19,102 @@ use xmas_elf::program::{self, ProgramHeader64};
use super::elf_file::Elf; use super::elf_file::Elf;
use super::elf_segment_pager::ElfSegmentPager; use super::elf_segment_pager::ElfSegmentPager;
use super::init_stack::InitStack;
/// load elf to the root vmar. this function will
/// 1. read the vaddr of each segment to get all elf pages.
/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar.
/// 3. write proper content to the init stack.
pub fn load_elf_to_root_vmar(
file_header: &[u8],
elf_file: Arc<FileHandle>,
root_vmar: &Vmar<Full>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<ElfLoadInfo> {
let elf = Elf::parse_elf(file_header)?;
map_segment_vmos(&elf, root_vmar, elf_file)?;
let mut init_stack = InitStack::new_default_config(argv, envp);
init_stack.init(root_vmar, &elf)?;
let elf_load_info = ElfLoadInfo::new(elf.entry_point(), init_stack.user_stack_top());
debug!("load elf succeeds.");
Ok(elf_load_info)
}
pub struct ElfLoadInfo { pub struct ElfLoadInfo {
segments: Vec<ElfSegment>, entry_point: Vaddr,
init_stack: InitStack, user_stack_top: Vaddr,
elf_header_info: ElfHeaderInfo,
}
pub struct ElfSegment {
/// The virtual addr where to put the segment.
pub virtual_addr: Vaddr,
/// The segment's size in memory, in bytes.
pub mem_size: usize,
/// The segment's offset in origin elf file
pub offset: usize,
/// The size the segment has in origin elf file, in bytes
pub file_size: usize,
type_: program::Type,
vm_perm: VmPerm,
}
#[derive(Debug, Clone, Copy, Default)]
/// Info parsed from elf header. The entry point is used to set rip
/// The other info is used to set auxv vectors.
pub struct ElfHeaderInfo {
/// the entry point of the elf
pub entry_point: Vaddr,
/// page header table offset
pub ph_off: u64,
/// number of program headers
pub ph_num: u16,
/// The size of a program header
pub ph_ent: u16,
}
impl ElfSegment {
fn parse_elf_segment(program_header: ProgramHeader64) -> Result<Self> {
let start = program_header.virtual_addr as Vaddr;
let end = start + program_header.mem_size as Vaddr;
let type_ = program_header
.get_type()
.map_err(|_| Error::new(Errno::ENOEXEC))?;
let vm_perm = Self::parse_segment_perm(program_header.flags)?;
Ok(Self {
virtual_addr: program_header.virtual_addr as _,
mem_size: program_header.mem_size as usize,
offset: program_header.offset as usize,
file_size: program_header.file_size as usize,
type_,
vm_perm,
})
}
pub fn parse_segment_perm(flags: xmas_elf::program::Flags) -> Result<VmPerm> {
if !flags.is_read() {
return_errno_with_message!(Errno::ENOEXEC, "unreadable segment");
}
let mut vm_perm = VmPerm::R;
if flags.is_write() {
vm_perm |= VmPerm::W;
}
if flags.is_execute() {
vm_perm |= VmPerm::X;
}
Ok(vm_perm)
}
fn contains_program_headers_table(&self, ph_offset: usize) -> bool {
// program headers table is at ph_offset of elf file
self.offset <= ph_offset && ph_offset < self.offset + self.file_size
}
/// If this segment contains ph table, then returns the ph table addr
/// Otherwise, returns None
pub fn program_headers_table_addr(&self, ph_offset: usize) -> Option<Vaddr> {
if self.contains_program_headers_table(ph_offset) {
Some(ph_offset - self.offset + self.virtual_addr)
} else {
None
}
}
pub fn is_loadable(&self) -> bool {
self.type_ == program::Type::Load
}
pub fn start_address(&self) -> Vaddr {
self.virtual_addr
}
pub fn end_address(&self) -> Vaddr {
self.virtual_addr + self.mem_size
}
pub fn init_segment_vmo(&self, elf_file_content: &'static [u8]) -> Vmo<Full> {
let vmo_start = self.start_address().align_down(PAGE_SIZE);
let vmo_end = self.end_address().align_up(PAGE_SIZE);
let segment_len = vmo_end - vmo_start;
let pager = Arc::new(ElfSegmentPager::new(elf_file_content, self)) as Arc<dyn Pager>;
let vmo_alloc_options: VmoOptions<Full> = VmoOptions::new(segment_len).pager(pager);
vmo_alloc_options.alloc().unwrap()
}
// create vmo for each segment and map the segment to root_vmar
fn map_segment_vmo(
&self,
root_vmar: &Vmar<Full>,
elf_file_content: &'static [u8],
) -> Result<()> {
let vmo = self.init_segment_vmo(elf_file_content).to_dyn();
let perms = VmPerms::from(self.vm_perm);
// The segment may not be aligned to page
let offset = self.start_address().align_down(PAGE_SIZE);
let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset);
let map_addr = vm_map_options.build()?;
Ok(())
}
} }
impl ElfLoadInfo { impl ElfLoadInfo {
fn with_capacity( pub fn new(entry_point: Vaddr, user_stack_top: Vaddr) -> Self {
capacity: usize,
init_stack: InitStack,
elf_header_info: ElfHeaderInfo,
) -> Self {
Self { Self {
segments: Vec::with_capacity(capacity),
init_stack,
elf_header_info,
}
}
fn add_segment(&mut self, elf_segment: ElfSegment) {
self.segments.push(elf_segment);
}
pub fn parse_elf_data(
elf_file_content: &'static [u8],
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<Self> {
let elf_file = Elf::parse_elf(elf_file_content)?;
// parse elf header
let elf_header_info = ElfHeaderInfo::parse_elf_header(&elf_file);
// FIXME: only contains load segment?
let ph_count = elf_file.program_headers.len();
let init_stack = InitStack::new_default_config(argv, envp);
let mut elf_load_info = ElfLoadInfo::with_capacity(ph_count, init_stack, elf_header_info);
// parse each segemnt
for program_header in elf_file.program_headers {
let elf_segment = ElfSegment::parse_elf_segment(program_header)?;
if elf_segment.is_loadable() {
elf_load_info.add_segment(elf_segment)
}
}
Ok(elf_load_info)
}
/// init vmo for each segment and then map segment to root vmar
pub fn map_segment_vmos(
&self,
root_vmar: &Vmar<Full>,
elf_file_content: &'static [u8],
) -> Result<()> {
for segment in &self.segments {
segment.map_segment_vmo(root_vmar, elf_file_content)?;
}
Ok(())
}
pub fn init_stack(&mut self, root_vmar: &Vmar<Full>, file_content: &[u8]) -> Result<()> {
let ph_addr = self.program_headers_table_addr()?;
self.init_stack
.init(root_vmar, &self.elf_header_info, ph_addr)?;
Ok(())
}
fn program_headers_table_addr(&self) -> Result<Vaddr> {
let ph_offset = self.elf_header_info.ph_off as usize;
for segment in &self.segments {
if let Some(ph_addr) = segment.program_headers_table_addr(ph_offset) {
return Ok(ph_addr);
}
}
return_errno_with_message!(
Errno::ENOEXEC,
"can not find program header table address in elf"
);
}
pub fn entry_point(&self) -> u64 {
self.elf_header_info.entry_point as u64
}
pub fn user_stack_top(&self) -> u64 {
self.init_stack.user_stack_top() as u64
}
pub fn argc(&self) -> u64 {
self.init_stack.argc()
}
pub fn argv(&self) -> u64 {
self.init_stack.argv()
}
pub fn envc(&self) -> u64 {
self.init_stack.envc()
}
pub fn envp(&self) -> u64 {
self.init_stack.envp()
}
}
impl ElfHeaderInfo {
fn parse_elf_header(elf_file: &Elf) -> Self {
let entry_point = elf_file.elf_header.pt2.entry_point as Vaddr;
let ph_off = elf_file.elf_header.pt2.ph_offset;
let ph_num = elf_file.elf_header.pt2.ph_count;
let ph_ent = elf_file.elf_header.pt2.ph_entry_size;
ElfHeaderInfo {
entry_point, entry_point,
ph_off, user_stack_top,
ph_num,
ph_ent,
} }
} }
pub fn entry_point(&self) -> Vaddr {
self.entry_point
}
pub fn user_stack_top(&self) -> Vaddr {
self.user_stack_top
}
}
/// init vmo for each segment and then map segment to root vmar
pub fn map_segment_vmos(
elf: &Elf,
root_vmar: &Vmar<Full>,
elf_file: Arc<FileHandle>,
) -> Result<()> {
for program_header in &elf.program_headers {
let type_ = program_header
.get_type()
.map_err(|_| Error::new(Errno::ENOEXEC))?;
if type_ == program::Type::Load {
let vmo = init_segment_vmo(program_header, elf_file.clone())?;
map_segment_vmo(program_header, vmo, root_vmar, elf_file.clone())?;
}
}
Ok(())
}
/// map the segment vmo to root_vmar
fn map_segment_vmo(
program_header: &ProgramHeader64,
vmo: Vmo,
root_vmar: &Vmar<Full>,
elf_file: Arc<FileHandle>,
) -> Result<()> {
let perms = VmPerms::from(parse_segment_perm(program_header.flags)?);
let offset = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
let vm_map_options = root_vmar.new_map(vmo, perms)?.offset(offset);
let map_addr = vm_map_options.build()?;
Ok(())
}
/// create vmo for each segment
fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: Arc<FileHandle>) -> Result<Vmo> {
let vmo_start = (program_header.virtual_addr as Vaddr).align_down(PAGE_SIZE);
let vmo_end = (program_header.virtual_addr as Vaddr + program_header.mem_size as Vaddr)
.align_up(PAGE_SIZE);
let segment_len = vmo_end - vmo_start;
let pager = Arc::new(ElfSegmentPager::new(elf_file, &program_header)) as Arc<dyn Pager>;
let vmo_alloc_options: VmoOptions<Full> = VmoOptions::new(segment_len).pager(pager);
Ok(vmo_alloc_options.alloc()?.to_dyn())
}
fn parse_segment_perm(flags: xmas_elf::program::Flags) -> Result<VmPerm> {
if !flags.is_read() {
return_errno_with_message!(Errno::ENOEXEC, "unreadable segment");
}
let mut vm_perm = VmPerm::R;
if flags.is_write() {
vm_perm |= VmPerm::W;
}
if flags.is_execute() {
vm_perm |= VmPerm::X;
}
Ok(vm_perm)
} }

View File

@ -1,26 +1,8 @@
pub mod aux_vec; mod aux_vec;
pub mod elf_file; mod elf_file;
pub mod elf_segment_pager; mod elf_segment_pager;
pub mod init_stack; mod init_stack;
pub mod load_elf; mod load_elf;
use self::load_elf::ElfLoadInfo; pub use init_stack::INIT_STACK_SIZE;
use crate::{prelude::*, rights::Full, vm::vmar::Vmar}; pub use load_elf::{load_elf_to_root_vmar, ElfLoadInfo};
/// load elf to the root vmar. this function will
/// 1. read the vaddr of each segment to get all elf pages.
/// 2. create a vmo for each elf segment, create a backup pager for each segment. Then map the vmo to the root vmar.
/// 3. write proper content to the init stack.
pub fn load_elf_to_root_vmar(
elf_file_content: &'static [u8],
root_vmar: &Vmar<Full>,
argv: Vec<CString>,
envp: Vec<CString>,
) -> Result<ElfLoadInfo> {
let mut elf_load_info = ElfLoadInfo::parse_elf_data(elf_file_content, argv, envp)?;
elf_load_info.map_segment_vmos(root_vmar, elf_file_content)?;
elf_load_info.init_stack(root_vmar, elf_file_content)?;
debug!("load elf succeeds.");
Ok(elf_load_info)
}

View File

@ -1,5 +1,6 @@
use core::sync::atomic::{AtomicI32, Ordering}; use core::sync::atomic::{AtomicI32, Ordering};
use self::elf::{ElfLoadInfo, load_elf_to_root_vmar};
use self::posix_thread::posix_thread_ext::PosixThreadExt; use self::posix_thread::posix_thread_ext::PosixThreadExt;
use self::process_group::ProcessGroup; use self::process_group::ProcessGroup;
use self::process_vm::user_heap::UserHeap; use self::process_vm::user_heap::UserHeap;
@ -10,16 +11,18 @@ use self::signal::sig_disposition::SigDispositions;
use self::signal::sig_queues::SigQueues; use self::signal::sig_queues::SigQueues;
use self::signal::signals::kernel::KernelSignal; use self::signal::signals::kernel::KernelSignal;
use self::status::ProcessStatus; use self::status::ProcessStatus;
use crate::fs::file_handle::FileHandle;
use crate::fs::file_table::FileTable; use crate::fs::file_table::FileTable;
use crate::fs::fs_resolver::AT_FDCWD;
use crate::fs::fs_resolver::{FsPath, FsResolver}; use crate::fs::fs_resolver::{FsPath, FsResolver};
use crate::fs::utils::AccessMode; use crate::fs::utils::{AccessMode, SeekFrom};
use crate::prelude::*; use crate::prelude::*;
use crate::rights::Full; use crate::rights::Full;
use crate::thread::{thread_table, Thread}; use crate::thread::{thread_table, Thread};
use crate::tty::get_n_tty; use crate::tty::get_n_tty;
use crate::vm::vmar::Vmar; use crate::vm::vmar::Vmar;
use alloc::string::String;
use jinux_frame::sync::WaitQueue; use jinux_frame::sync::WaitQueue;
use jinux_frame::task::Task;
pub mod clone; pub mod clone;
pub mod elf; pub mod elf;
@ -45,7 +48,7 @@ const INIT_PROCESS_PID: Pid = 1;
pub struct Process { pub struct Process {
// Immutable Part // Immutable Part
pid: Pid, pid: Pid,
elf_path: Option<CString>, executable_path: Option<String>,
user_vm: Option<UserVm>, user_vm: Option<UserVm>,
root_vmar: Arc<Vmar<Full>>, root_vmar: Arc<Vmar<Full>>,
/// wait for child status changed /// wait for child status changed
@ -96,7 +99,7 @@ impl Process {
pid: Pid, pid: Pid,
parent: Weak<Process>, parent: Weak<Process>,
threads: Vec<Arc<Thread>>, threads: Vec<Arc<Thread>>,
elf_path: Option<CString>, executable_path: Option<String>,
user_vm: Option<UserVm>, user_vm: Option<UserVm>,
root_vmar: Arc<Vmar<Full>>, root_vmar: Arc<Vmar<Full>>,
process_group: Weak<ProcessGroup>, process_group: Weak<ProcessGroup>,
@ -111,7 +114,7 @@ impl Process {
Self { Self {
pid, pid,
threads: Mutex::new(threads), threads: Mutex::new(threads),
elf_path, executable_path,
user_vm, user_vm,
root_vmar, root_vmar,
waiting_children, waiting_children,
@ -139,12 +142,11 @@ impl Process {
/// init a user process and run the process /// init a user process and run the process
pub fn spawn_user_process( pub fn spawn_user_process(
filename: CString, filename: String,
elf_file_content: &'static [u8],
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
) -> Arc<Self> { ) -> Arc<Self> {
let process = Process::create_user_process(filename, elf_file_content, argv, envp); let process = Process::create_user_process(filename, argv, envp);
// FIXME: How to determine the fg process group? // FIXME: How to determine the fg process group?
let pgid = process.pgid(); let pgid = process.pgid();
// FIXME: tty should be a parameter? // FIXME: tty should be a parameter?
@ -155,24 +157,19 @@ impl Process {
} }
fn create_user_process( fn create_user_process(
elf_path: CString, executable_path: String,
elf_file_content: &'static [u8],
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
) -> Arc<Self> { ) -> Arc<Self> {
let user_process = Arc::new_cyclic(|weak_process_ref| { let user_process = Arc::new_cyclic(|weak_process_ref| {
let weak_process = weak_process_ref.clone(); let weak_process = weak_process_ref.clone();
let cloned_filename = Some(elf_path.clone()); let cloned_filename = Some(executable_path.clone());
let root_vmar = Vmar::<Full>::new_root().unwrap(); let root_vmar = Vmar::<Full>::new_root().unwrap();
<<<<<<< HEAD
let thread = Thread::new_posix_thread_from_elf(
=======
let fs = FsResolver::new(); let fs = FsResolver::new();
let thread = Thread::new_posix_thread_from_executable( let thread = Thread::new_posix_thread_from_executable(
>>>>>>> 0255134... fix
&root_vmar, &root_vmar,
elf_path, &fs,
elf_file_content, executable_path,
weak_process, weak_process,
argv, argv,
envp, envp,
@ -362,8 +359,8 @@ impl Process {
self.children.lock().len() != 0 self.children.lock().len() != 0
} }
pub fn filename(&self) -> Option<&CString> { pub fn executable_path(&self) -> Option<&String> {
self.elf_path.as_ref() self.executable_path.as_ref()
} }
pub fn status(&self) -> &Mutex<ProcessStatus> { pub fn status(&self) -> &Mutex<ProcessStatus> {
@ -387,77 +384,3 @@ impl Process {
pub fn get_init_process() -> Option<Arc<Process>> { pub fn get_init_process() -> Option<Arc<Process>> {
process_table::pid_to_process(INIT_PROCESS_PID) process_table::pid_to_process(INIT_PROCESS_PID)
} }
/// Set up root vmar for an executable.
/// About recursion_limit: recursion limit is used to limit th recursion depth of shebang executables.
/// If the interpreter program(the program behind !#) of shebang executable is also a shebang,
/// then it will trigger recursion. We will try to setup root vmar for the interpreter program.
/// I guess for most cases, setting the recursion_limit as 1 should be enough.
/// because the interpreter game is usually an elf binary(e.g., /bin/bash)
pub fn setup_root_vmar(
executable_path: String,
argv: Vec<CString>,
envp: Vec<CString>,
fs_resolver: &FsResolver,
root_vmar: Vmar<Full>,
recursion_limit: usize,
) -> Result<()> {
use crate::fs::fs_resolver::AT_FDCWD;
let fs_path = FsPath::new(AT_FDCWD, &executable_path)?;
let file = fs_resolver.open(&fs_path, AccessMode::O_RDONLY as u32, 0)?;
// read the first page of file header
let mut file_header_buffer = [0u8; PAGE_SIZE];
file.read(&mut file_header_buffer)?;
if recursion_limit > 0
&& file_header_buffer.starts_with(b"!#")
&& file_header_buffer.contains(&b'\n')
{
return set_up_root_vmar_for_shebang(
argv,
envp,
&file_header_buffer,
fs_resolver,
root_vmar,
recursion_limit,
);
}
todo!()
}
fn set_up_root_vmar_for_shebang(
argv: Vec<CString>,
envp: Vec<CString>,
file_header_buffer: &[u8],
fs_resolver: &FsResolver,
root_vmar: Vmar<Full>,
recursion_limit: usize,
) -> Result<()> {
let first_line_len = file_header_buffer.iter().position(|&c| c == b'\n').unwrap();
// skip !#
let shebang_header = &file_header_buffer[2..first_line_len];
let mut shebang_argv = Vec::new();
for arg in shebang_header.split(|&c| c == b' ') {
let arg = CString::new(arg)?;
shebang_argv.push(arg);
}
if shebang_argv.len() != 1 {
return_errno_with_message!(
Errno::EINVAL,
"One and only one intpreter program should be specified"
);
}
for origin_arg in argv.into_iter() {
shebang_argv.push(origin_arg);
}
use alloc::string::ToString;
let shebang_path = shebang_argv[0].to_str()?.to_string();
setup_root_vmar(
shebang_path,
shebang_argv,
envp,
fs_resolver,
root_vmar,
recursion_limit - 1,
)
}

View File

@ -14,10 +14,9 @@ impl ThreadName {
} }
} }
pub fn new_from_elf_path(elf_path: &CStr) -> Result<Self> { pub fn new_from_executable_path(elf_path: &str) -> Result<Self> {
let mut thread_name = ThreadName::new(); let mut thread_name = ThreadName::new();
let elf_file_name = elf_path let elf_file_name = elf_path
.to_str()?
.split('/') .split('/')
.last() .last()
.ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?; .ok_or(Error::with_message(Errno::EINVAL, "invalid elf path"))?;

View File

@ -1,8 +1,10 @@
use alloc::string::String;
use jinux_frame::{cpu::CpuContext, user::UserSpace}; use jinux_frame::{cpu::CpuContext, user::UserSpace};
use crate::{ use crate::{
fs::fs_resolver::FsResolver,
prelude::*, prelude::*,
process::{elf::load_elf_to_root_vmar, Process}, process::{setup_root_vmar, Process},
rights::Full, rights::Full,
thread::{allocate_tid, Thread}, thread::{allocate_tid, Thread},
vm::vmar::Vmar, vm::vmar::Vmar,
@ -13,8 +15,8 @@ pub trait PosixThreadExt {
fn as_posix_thread(&self) -> Option<&PosixThread>; fn as_posix_thread(&self) -> Option<&PosixThread>;
fn new_posix_thread_from_executable( fn new_posix_thread_from_executable(
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
elf_path: CString, fs_resolver: &FsResolver,
elf_file_content: &'static [u8], executable_path: String,
process: Weak<Process>, process: Weak<Process>,
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
@ -25,20 +27,28 @@ impl PosixThreadExt for Thread {
/// This function should only be called when launch shell() /// This function should only be called when launch shell()
fn new_posix_thread_from_executable( fn new_posix_thread_from_executable(
root_vmar: &Vmar<Full>, root_vmar: &Vmar<Full>,
elf_path: CString, fs_resolver: &FsResolver,
elf_file_content: &'static [u8], executable_path: String,
process: Weak<Process>, process: Weak<Process>,
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
) -> Arc<Self> { ) -> Arc<Self> {
let elf_load_info = load_elf_to_root_vmar(elf_file_content, &root_vmar, argv, envp) let elf_load_info = setup_root_vmar(
.expect("Load Elf failed"); executable_path.clone(),
argv,
envp,
fs_resolver,
root_vmar,
1,
)
.unwrap();
let vm_space = root_vmar.vm_space().clone(); let vm_space = root_vmar.vm_space().clone();
let mut cpu_ctx = CpuContext::default(); let mut cpu_ctx = CpuContext::default();
cpu_ctx.set_rip(elf_load_info.entry_point()); cpu_ctx.set_rip(elf_load_info.entry_point() as _);
cpu_ctx.set_rsp(elf_load_info.user_stack_top()); cpu_ctx.set_rsp(elf_load_info.user_stack_top() as _);
let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx)); let user_space = Arc::new(UserSpace::new(vm_space, cpu_ctx));
let thread_name = Some(ThreadName::new_from_elf_path(&elf_path).unwrap()); let thread_name = Some(ThreadName::new_from_executable_path(&executable_path).unwrap());
let tid = allocate_tid(); let tid = allocate_tid();
let thread_builder = PosixThreadBuilder::new(tid, user_space) let thread_builder = PosixThreadBuilder::new(tid, user_space)
.thread_name(thread_name) .thread_name(thread_name)

View File

@ -4,7 +4,7 @@
use crate::prelude::*; use crate::prelude::*;
use super::{elf::init_stack::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT}; use super::{elf::INIT_STACK_SIZE, process_vm::user_heap::USER_HEAP_SIZE_LIMIT};
pub struct ResourceLimits { pub struct ResourceLimits {
rlimits: [RLimit64; RLIMIT_COUNT], rlimits: [RLimit64; RLIMIT_COUNT],

View File

@ -31,7 +31,7 @@ pub fn handle_pending_signal(context: &mut CpuContext) -> Result<()> {
let current_thread = current_thread!(); let current_thread = current_thread!();
let posix_thread = current_thread.as_posix_thread().unwrap(); let posix_thread = current_thread.as_posix_thread().unwrap();
let pid = current.pid(); let pid = current.pid();
let process_name = current.filename().unwrap(); let process_name = current.executable_path().unwrap();
let sig_mask = posix_thread.sig_mask().lock().clone(); let sig_mask = posix_thread.sig_mask().lock().clone();
let mut thread_sig_queues = posix_thread.sig_queues().lock(); let mut thread_sig_queues = posix_thread.sig_queues().lock();
let mut proc_sig_queues = current.sig_queues().lock(); let mut proc_sig_queues = current.sig_queues().lock();

View File

@ -2,9 +2,9 @@ use jinux_frame::cpu::CpuContext;
use super::{constants::*, SyscallReturn}; use super::{constants::*, SyscallReturn};
use crate::log_syscall_entry; use crate::log_syscall_entry;
use crate::process::elf::load_elf_to_root_vmar;
use crate::process::posix_thread::name::ThreadName; use crate::process::posix_thread::name::ThreadName;
use crate::process::posix_thread::posix_thread_ext::PosixThreadExt; use crate::process::posix_thread::posix_thread_ext::PosixThreadExt;
use crate::process::setup_root_vmar;
use crate::util::{read_cstring_from_user, read_val_from_user}; use crate::util::{read_cstring_from_user, read_val_from_user};
use crate::{prelude::*, syscall::SYS_EXECVE}; use crate::{prelude::*, syscall::SYS_EXECVE};
@ -15,24 +15,22 @@ pub fn sys_execve(
context: &mut CpuContext, context: &mut CpuContext,
) -> Result<SyscallReturn> { ) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_EXECVE); log_syscall_entry!(SYS_EXECVE);
let elf_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?; let executable_path = read_cstring_from_user(filename_ptr, MAX_FILENAME_LEN)?;
let executable_path = executable_path.into_string().unwrap();
let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?; let argv = read_cstring_vec(argv_ptr_ptr, MAX_ARGV_NUMBER, MAX_ARG_LEN)?;
let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?; let envp = read_cstring_vec(envp_ptr_ptr, MAX_ENVP_NUMBER, MAX_ENV_LEN)?;
debug!( debug!(
"filename: {:?}, argv = {:?}, envp = {:?}", "filename: {:?}, argv = {:?}, envp = {:?}",
elf_path, argv, envp executable_path, argv, envp
); );
if elf_path != CString::new("./hello").unwrap() {
panic!("Unknown filename.");
}
// FIXME: should we set thread name in execve? // FIXME: should we set thread name in execve?
let current_thread = current_thread!(); let current_thread = current_thread!();
let posix_thread = current_thread.as_posix_thread().unwrap(); let posix_thread = current_thread.as_posix_thread().unwrap();
let mut thread_name = posix_thread.thread_name().lock(); let mut thread_name = posix_thread.thread_name().lock();
let new_thread_name = ThreadName::new_from_elf_path(&elf_path)?; let new_thread_name = ThreadName::new_from_executable_path(&executable_path)?;
*thread_name = Some(new_thread_name); *thread_name = Some(new_thread_name);
let elf_file_content = crate::user_apps::read_execve_hello_content(); // let elf_file_content = crate::user_apps::read_execve_hello_content();
let current = current!(); let current = current!();
// destroy root vmars // destroy root vmars
let root_vmar = current.root_vmar(); let root_vmar = current.root_vmar();
@ -42,8 +40,8 @@ pub fn sys_execve(
.expect("[Internal Error] User process should have user vm"); .expect("[Internal Error] User process should have user vm");
user_vm.set_default(); user_vm.set_default();
// load elf content to new vm space // load elf content to new vm space
let elf_load_info = let fs_resolver = &*current.fs().read();
load_elf_to_root_vmar(elf_file_content, root_vmar, argv, envp).expect("load elf failed"); let elf_load_info = setup_root_vmar(executable_path, argv, envp, fs_resolver, root_vmar, 1)?;
debug!("load elf in execve succeeds"); debug!("load elf in execve succeeds");
// set signal disposition to default // set signal disposition to default
current.sig_dispositions().lock().inherit(); current.sig_dispositions().lock().inherit();
@ -53,10 +51,10 @@ pub fn sys_execve(
context.fs_base = defalut_content.fs_base; context.fs_base = defalut_content.fs_base;
context.fp_regs = defalut_content.fp_regs; context.fp_regs = defalut_content.fp_regs;
// set new entry point // set new entry point
context.gp_regs.rip = elf_load_info.entry_point(); context.gp_regs.rip = elf_load_info.entry_point() as _;
debug!("entry_point: 0x{:x}", elf_load_info.entry_point()); debug!("entry_point: 0x{:x}", elf_load_info.entry_point());
// set new user stack top // set new user stack top
context.gp_regs.rsp = elf_load_info.user_stack_top(); context.gp_regs.rsp = elf_load_info.user_stack_top() as _;
debug!("user stack top: 0x{:x}", elf_load_info.user_stack_top()); debug!("user stack top: 0x{:x}", elf_load_info.user_stack_top());
Ok(SyscallReturn::NoReturn) Ok(SyscallReturn::NoReturn)
} }

View File

@ -26,7 +26,7 @@ pub fn sys_readlinkat(
let current = current!(); let current = current!();
if pathname == CString::new("/proc/self/exe")? { if pathname == CString::new("/proc/self/exe")? {
// "proc/self/exe" is used to read the filename of current executable // "proc/self/exe" is used to read the filename of current executable
let process_file_name = current.filename().unwrap(); let process_file_name = current.executable_path().unwrap();
debug!("process exec filename= {:?}", process_file_name); debug!("process exec filename= {:?}", process_file_name);
// readlink does not append a terminating null byte to buf // readlink does not append a terminating null byte to buf
let bytes = process_file_name.as_bytes(); let bytes = process_file_name.as_bytes();

View File

@ -1,44 +1,21 @@
use crate::fs::{
fs_resolver::{FsPath, FsResolver},
utils::AccessMode,
};
use crate::prelude::*; use crate::prelude::*;
pub struct UserApp { pub struct UserApp {
pub elf_path: CString, pub executable_path: String,
pub app_content: Vec<u8>,
pub argv: Vec<CString>, pub argv: Vec<CString>,
pub envp: Vec<CString>, pub envp: Vec<CString>,
} }
impl UserApp { impl UserApp {
pub fn new(elf_path: &str) -> Result<Self> { pub fn new(executable_path: &str) -> Result<Self> {
let app_name = CString::new(elf_path).unwrap(); let app_name = String::from(executable_path);
let app_content = { let arg0 = CString::new(executable_path)?;
let fs = FsResolver::new();
let file = fs.open(&FsPath::try_from(elf_path)?, AccessMode::O_RDONLY as u32, 0)?;
let mut content = Vec::new();
let len = file.read_to_end(&mut content)?;
if len != file.len() {
return_errno_with_message!(Errno::EINVAL, "read len is not equal to file size");
}
content
};
Ok(UserApp { Ok(UserApp {
elf_path: app_name, executable_path: app_name,
app_content, argv: vec![arg0],
argv: Vec::new(),
envp: Vec::new(), envp: Vec::new(),
}) })
} }
pub fn set_argv(&mut self, argv: Vec<CString>) {
self.argv = argv;
}
pub fn set_envp(&mut self, envp: Vec<CString>) {
self.envp = envp;
}
} }
pub fn get_all_apps() -> Result<Vec<UserApp>> { pub fn get_all_apps() -> Result<Vec<UserApp>> {
@ -81,7 +58,7 @@ pub fn get_busybox_app() -> Result<UserApp> {
// busybox // busybox
let mut busybox = UserApp::new("/busybox/busybox")?; let mut busybox = UserApp::new("/busybox/busybox")?;
// -l option means the busybox is running as logging shell // -l option means the busybox is running as logging shell
let argv = ["/busybox", "sh", "-l"]; let argv = ["sh", "-l"];
let envp = [ let envp = [
"SHELL=/bin/sh", "SHELL=/bin/sh",
"PWD=/", "PWD=/",
@ -92,21 +69,13 @@ pub fn get_busybox_app() -> Result<UserApp> {
"OLDPWD=/", "OLDPWD=/",
]; ];
let argv = to_vec_cstring(&argv)?; let mut argv = to_vec_cstring(&argv)?;
let envp = to_vec_cstring(&envp)?; let mut envp = to_vec_cstring(&envp)?;
busybox.set_argv(argv); busybox.argv.append(&mut argv);
busybox.set_envp(envp); busybox.envp.append(&mut envp);
Ok(busybox) Ok(busybox)
} }
fn read_execve_content() -> &'static [u8] {
include_bytes!("../../../../apps/execve/execve")
}
pub fn read_execve_hello_content() -> &'static [u8] {
include_bytes!("../../../../apps/execve/hello")
}
fn to_vec_cstring(raw_strs: &[&str]) -> Result<Vec<CString>> { fn to_vec_cstring(raw_strs: &[&str]) -> Result<Vec<CString>> {
let mut res = Vec::new(); let mut res = Vec::new();
for raw_str in raw_strs { for raw_str in raw_strs {