diff --git a/kernel/Makefile b/kernel/Makefile index a110534..a963644 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,3 +1,10 @@ +# Commands: +# make build Build +# make run Build and run in QEMU +# make justrun Run the last build +# make doc Generate docs +# make asm Open the deassemble file of the last build +# make header Open 'objdump -h' of the last build # make addr2line Use addr2line to recover line info in backtrace # make clean Clean # diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs index f6c4f10..e018db3 100644 --- a/kernel/src/process/structs.rs +++ b/kernel/src/process/structs.rs @@ -3,7 +3,7 @@ use core::fmt; use log::*; use spin::{Mutex, RwLock}; -use xmas_elf::{ElfFile, header, program::{Flags, Type}}; +use xmas_elf::{ElfFile, header, program::{Flags, Type, SegmentData}}; use rcore_memory::PAGE_SIZE; use rcore_thread::Tid; use core::str; @@ -183,51 +183,41 @@ impl Thread { pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box where Iter: Iterator { - // Parse elf + // Parse ELF let elf = ElfFile::new(data).expect("failed to read elf"); let is32 = match elf.header.pt2 { header::HeaderPt2::Header32(_) => true, header::HeaderPt2::Header64(_) => false, }; + // Check ELF type match elf.header.pt2.type_().as_type() { - header::Type::Executable => { -// panic!("ELF is not shared object"); - }, + header::Type::Executable => {}, header::Type::SharedObject => {}, _ => panic!("ELF is not executable or shared object"), } - if let Some(interp) = elf.program_iter().filter(|ph| ph.get_type() == Ok(Type::Interp)).next() { - let offset = interp.offset() as usize; - let size = interp.file_size() as usize - 1; // skip last '\0' - if offset + size < data.len() { - if let Ok(loader_path) = str::from_utf8(&data[offset..(offset+size)]) { - // assuming absolute path - if let Ok(inode) = crate::fs::ROOT_INODE.lookup_follow(&loader_path[1..], FOLLOW_MAX_DEPTH) { - if let Ok(buf) = inode.read_as_vec() { - debug!("using loader {}", &loader_path); - // Elf loader should not have INTERP - // No infinite loop - let mut new_args: Vec<&str> = args.collect(); - new_args.insert(0, loader_path); - return Thread::new_user(buf.as_slice(), new_args.into_iter()); - } else { - warn!("loader specified as {} but failed to read", &loader_path); - } - } else { - warn!("loader specified as {} but not found", &loader_path); - } + // Check interpreter + if let Ok(loader_path) = elf.get_interpreter() { + // assuming absolute path + if let Ok(inode) = crate::fs::ROOT_INODE.lookup_follow(&loader_path[1..], FOLLOW_MAX_DEPTH) { + if let Ok(buf) = inode.read_as_vec() { + debug!("using loader {}", &loader_path); + // Elf loader should not have INTERP + // No infinite loop + let mut new_args: Vec<&str> = args.collect(); + new_args.insert(0, loader_path); + return Thread::new_user(buf.as_slice(), new_args.into_iter()); } else { - warn!("loader specified but not found"); + warn!("loader specified as {} but failed to read", &loader_path); } } else { - warn!("loader specified but invalid"); + warn!("loader specified as {} but not found", &loader_path); } } // Make page table - let (mut vm, entry_addr) = memory_set_from(&elf); + let mut vm = elf.make_memory_set(); // User stack use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER32_STACK_OFFSET}; @@ -240,20 +230,14 @@ impl Thread { ustack_top }; + // Make init info let init_info = ProcInitInfo { args: args.map(|s| String::from(s)).collect(), envs: BTreeMap::new(), auxv: { let mut map = BTreeMap::new(); - if let Some(phdr) = elf.program_iter() - .find(|ph| ph.get_type() == Ok(Type::Phdr)) { - // if phdr exists in program header, use it - map.insert(abi::AT_PHDR, phdr.virtual_addr() as usize); - } else if let Some(elf_addr) = elf.program_iter().find(|ph| ph.get_type() == Ok(Type::Load) && ph.offset() == 0) { - // otherwise, check if elf is loaded from the beginning, then phdr can be inferred. - map.insert(abi::AT_PHDR, elf_addr.virtual_addr() as usize + elf.header.pt2.ph_offset() as usize); - } else { - warn!("new_user: no phdr found, tls might not work"); + if let Some(phdr_vaddr) = elf.get_phdr_vaddr() { + map.insert(abi::AT_PHDR, phdr_vaddr as usize); } map.insert(abi::AT_PHENT, elf.header.pt2.ph_entry_size() as usize); map.insert(abi::AT_PHNUM, elf.header.pt2.ph_count() as usize); @@ -274,6 +258,8 @@ impl Thread { files.insert(1, FileLike::File(FileHandle::new(crate::fs::STDOUT.clone(), OpenOptions { read: false, write: true, append: false }))); files.insert(2, FileLike::File(FileHandle::new(crate::fs::STDOUT.clone(), OpenOptions { read: false, write: true, append: false }))); + let entry_addr = elf.header.pt2.entry_point() as usize; + Box::new(Thread { context: unsafe { Context::new_user_thread( @@ -299,9 +285,11 @@ impl Thread { /// Fork a new process from current one pub fn fork(&self, tf: &TrapFrame) -> Box { // Clone memory set, make a new page table - let vm = self.proc.lock().vm.clone(); - let files = self.proc.lock().files.clone(); - let cwd = self.proc.lock().cwd.clone(); + let proc = self.proc.lock(); + let vm = proc.vm.clone(); + let files = proc.files.clone(); + let cwd = proc.cwd.clone(); + drop(proc); let parent = Some(self.proc.clone()); debug!("fork: finish clone MemorySet"); @@ -324,7 +312,6 @@ impl Thread { } } - Box::new(Thread { context: unsafe { Context::new_fork(tf, kstack.top(), vm.token()) }, kstack, @@ -376,50 +363,6 @@ impl Process { } } - -/// Generate a MemorySet according to the ELF file. -/// Also return the real entry point address. -fn memory_set_from(elf: &ElfFile<'_>) -> (MemorySet, usize) { - debug!("creating MemorySet from ELF"); - let mut ms = MemorySet::new(); - let entry = elf.header.pt2.entry_point() as usize; - - // [NoMMU] Get total memory size and alloc space - let va_begin = elf.program_iter() - .filter(|ph| ph.get_type() == Ok(Type::Load)) - .map(|ph| ph.virtual_addr()).min().unwrap() as usize; - let va_end = elf.program_iter() - .filter(|ph| ph.get_type() == Ok(Type::Load)) - .map(|ph| ph.virtual_addr() + ph.mem_size()).max().unwrap() as usize; - let va_size = va_end - va_begin; - - for ph in elf.program_iter() { - if ph.get_type() != Ok(Type::Load) { - continue; - } - let virt_addr = ph.virtual_addr() as usize; - let offset = ph.offset() as usize; - let file_size = ph.file_size() as usize; - let mem_size = ph.mem_size() as usize; - - // Get target slice - let target = { - ms.push(virt_addr, virt_addr + mem_size, ph.flags().to_attr(), ByFrame::new(GlobalFrameAlloc), ""); - unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) } - }; - // Copy data - unsafe { - ms.with(|| { - if file_size != 0 { - target[..file_size].copy_from_slice(&elf.input[offset..offset + file_size]); - } - target[file_size..].iter_mut().for_each(|x| *x = 0); - }); - } - } - (ms, entry) -} - trait ToMemoryAttr { fn to_attr(&self) -> MemoryAttr; } @@ -432,3 +375,78 @@ impl ToMemoryAttr for Flags { flags } } + +/// Helper functions to process ELF file +trait ElfExt { + /// Generate a MemorySet according to the ELF file. + fn make_memory_set(&self) -> MemorySet; + + /// Get interpreter string if it has. + fn get_interpreter(&self) -> Result<&str, &str>; + + /// Get virtual address of PHDR section if it has. + fn get_phdr_vaddr(&self) -> Option; +} + +impl ElfExt for ElfFile<'_> { + fn make_memory_set(&self) -> MemorySet { + debug!("creating MemorySet from ELF"); + let mut ms = MemorySet::new(); + + for ph in self.program_iter() { + if ph.get_type() != Ok(Type::Load) { + continue; + } + let virt_addr = ph.virtual_addr() as usize; + let mem_size = ph.mem_size() as usize; + let data = match ph.get_data(self).unwrap() { + SegmentData::Undefined(data) => data, + _ => unreachable!(), + }; + + // Get target slice + let target = { + ms.push(virt_addr, virt_addr + mem_size, ph.flags().to_attr(), ByFrame::new(GlobalFrameAlloc), ""); + unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) } + }; + // Copy data + unsafe { + ms.with(|| { + if data.len() != 0 { + target[..data.len()].copy_from_slice(data); + } + target[data.len()..].iter_mut().for_each(|x| *x = 0); + }); + } + } + ms + } + + fn get_interpreter(&self) -> Result<&str, &str> { + let header = self.program_iter() + .filter(|ph| ph.get_type() == Ok(Type::Interp)) + .next().ok_or("no interp header")?; + let data = match header.get_data(self)? { + SegmentData::Undefined(data) => data, + _ => unreachable!(), + }; + let path = str::from_utf8(data) + .map_err(|_| "failed to convert to utf8")?; + Ok(path) + } + + fn get_phdr_vaddr(&self) -> Option { + if let Some(phdr) = self.program_iter() + .find(|ph| ph.get_type() == Ok(Type::Phdr)) { + // if phdr exists in program header, use it + Some(phdr.virtual_addr()) + } else if let Some(elf_addr) = self.program_iter() + .find(|ph| ph.get_type() == Ok(Type::Load) && ph.offset() == 0) { + // otherwise, check if elf is loaded from the beginning, then phdr can be inferred. + Some(elf_addr.virtual_addr() + self.header.pt2.ph_offset()) + } else { + warn!("elf: no phdr found, tls might not work"); + None + } + } +}