diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index a5ca35b2..e2989c6c 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -266,6 +266,10 @@ impl File for INodeFile { } impl INodeFile { + pub fn inode(&self) -> &Arc { + &self.inode + } + pub fn open(inode: Arc, abs_path: &str, flags: u32) -> Result { let access_mode = AccessMode::from_u32(flags)?; if (access_mode.readable() && !inode.allow_read()?) { diff --git a/src/libos/src/fs/procfs/pid/maps.rs b/src/libos/src/fs/procfs/pid/maps.rs new file mode 100644 index 00000000..bf788399 --- /dev/null +++ b/src/libos/src/fs/procfs/pid/maps.rs @@ -0,0 +1,134 @@ +use super::*; + +use crate::vm::{ChunkType, VMArea, VMPerms, VMRange}; + +// This file is to implement /proc/self(pid)/maps file system. +// +// Print format: +// vmrange_start-vmrange_end, permission, shared/private, offset, device ID, inode, pathname +// +// Example: +// - cat /proc/self/maps +// 555555554000-555555556000 r--p 00000000 08:12 39321752 /usr/bin/cat +// 555555556000-55555555b000 r-xp 00002000 08:12 39321752 /usr/bin/cat +// 55555555b000-55555555e000 r--p 00007000 08:12 39321752 /usr/bin/cat +// 55555555e000-55555555f000 r--p 00009000 08:12 39321752 /usr/bin/cat +// 55555555f000-555555560000 rw-p 0000a000 08:12 39321752 /usr/bin/cat +// 555555560000-555555581000 rw-p 00000000 00:00 0 [heap] +// 7ffff7536000-7ffff7558000 rw-p 00000000 00:00 0 +// 7ffff7558000-7ffff7dc8000 r--p 00000000 08:12 39322175 /usr/lib/locale/locale-archive +// 7ffff7dc8000-7ffff7dea000 r--p 00000000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so +// 7ffff7dea000-7ffff7f62000 r-xp 00022000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so +// 7ffff7f62000-7ffff7fb0000 r--p 0019a000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so +// 7ffff7fb0000-7ffff7fb4000 r--p 001e7000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so +// 7ffff7fb4000-7ffff7fb6000 rw-p 001eb000 08:12 39324754 /usr/lib/x86_64-linux-gnu/libc-2.31.so +// 7ffff7fb6000-7ffff7fbc000 rw-p 00000000 00:00 0 +// 7ffff7fcf000-7ffff7fd0000 r--p 00000000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so +// 7ffff7fd0000-7ffff7ff3000 r-xp 00001000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so +// 7ffff7ff3000-7ffff7ffb000 r--p 00024000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so +// 7ffff7ffc000-7ffff7ffd000 r--p 0002c000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so +// 7ffff7ffd000-7ffff7ffe000 rw-p 0002d000 08:12 39324750 /usr/lib/x86_64-linux-gnu/ld-2.31.so +// 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 +// 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] +// 80000006b000-80000006f000 r--p 00000000 00:00 0 [vvar] +// 80000006f000-800000071000 r-xp 00000000 00:00 0 [vdso] +// ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] +// +// Known limitation: +// - Device ID is not provided by FS +// - Not shown in address order + +pub struct ProcMapsINode(ProcessRef); + +impl ProcMapsINode { + pub fn new(process_ref: &ProcessRef) -> Arc { + Arc::new(File::new(Self(Arc::clone(process_ref)))) + } +} + +impl ProcINode for ProcMapsINode { + fn generate_data_in_bytes(&self) -> vfs::Result> { + let result_string = { + let main_thread = self.0.main_thread().unwrap(); + let process_vm = main_thread.vm(); + let heap_range = process_vm.heap_range(); + let stack_range = process_vm.stack_range(); + + let process_vm_chunks = process_vm.mem_chunks().read().unwrap(); + process_vm_chunks + .iter() + .map(|chunk| match chunk.internal() { + ChunkType::SingleVMA(vma) => { + let range = chunk.range(); + let heap_or_stack = if range == heap_range { + Some(" [heap]") + } else if range == stack_range { + Some(" [stack]") + } else { + None + }; + let vma = vma.lock().unwrap(); + get_output_for_vma(&vma, heap_or_stack) + } + ChunkType::MultiVMA(internal_manager) => { + let internal = internal_manager.lock().unwrap(); + let vmas_list = internal.chunk_manager().vmas(); + vmas_list + .iter() + .map(|obj| get_output_for_vma(obj.vma(), None)) + .fold(String::new(), |acc, vma_info| acc + &vma_info) + } + }) + .fold(String::new(), |acc, vma_info| acc + &vma_info) + }; + + Ok(result_string.into_bytes()) + } +} + +fn get_output_for_vma(vma: &VMArea, heap_or_stack: Option<&str>) -> String { + let range = vma.range(); + let perms = vma.perms(); + + let (file_path, offset, device_id, inode_num) = { + if let Some((file, offset)) = vma.init_file() { + let inode_file = file.as_inode_file().unwrap(); + let file_path = inode_file.abs_path(); + let inode_num = inode_file.inode().metadata().unwrap().inode; + let device_id = inode_file.inode().metadata().unwrap().dev; + (file_path, offset, device_id, inode_num) + } else if heap_or_stack.is_some() { + (heap_or_stack.unwrap(), 0, 0, 0) + } else { + ("", 0, 0, 0) + } + }; + + let shared = vma.writeback_file().is_some(); + print_each_map( + range, perms, shared, offset, device_id, inode_num, file_path, + ) +} + +fn print_each_map( + range: &VMRange, + perms: VMPerms, + shared: bool, + offset: usize, + device_id: usize, + inode_num: usize, + file_path: &str, +) -> String { + let result_str = format!( + "{:x}-{:x} {}{} {:08x} {} {} {}\n", + range.start(), + range.end(), + perms.display(), + if shared { "s" } else { "p" }, + offset, + device_id, + inode_num, + file_path + ); + result_str +} diff --git a/src/libos/src/fs/procfs/pid/mod.rs b/src/libos/src/fs/procfs/pid/mod.rs index b2e55a04..ab82d214 100644 --- a/src/libos/src/fs/procfs/pid/mod.rs +++ b/src/libos/src/fs/procfs/pid/mod.rs @@ -7,6 +7,7 @@ use self::comm::ProcCommINode; use self::cwd::ProcCwdSymINode; use self::exe::ProcExeSymINode; use self::fd::LockedProcFdDirINode; +use self::maps::ProcMapsINode; use self::root::ProcRootSymINode; use self::stat::ProcStatINode; @@ -15,6 +16,7 @@ mod comm; mod cwd; mod exe; mod fd; +mod maps; mod root; mod stat; @@ -60,6 +62,9 @@ impl LockedPidDirINode { // stat let stat_inode = ProcStatINode::new(&file.process_ref); file.entries.insert(String::from("stat"), stat_inode); + // maps + let maps_inode = ProcMapsINode::new(&file.process_ref); + file.entries.insert(String::from("maps"), maps_inode); Ok(()) } diff --git a/src/libos/src/process/do_spawn/exec_loader.rs b/src/libos/src/process/do_spawn/exec_loader.rs index 7ac35ea9..89cf09f5 100644 --- a/src/libos/src/process/do_spawn/exec_loader.rs +++ b/src/libos/src/process/do_spawn/exec_loader.rs @@ -1,6 +1,6 @@ use super::super::elf_file::*; use super::ThreadRef; -use crate::fs::{FileMode, INodeExt}; +use crate::fs::{AsINodeFile, FileMode, INodeExt}; use crate::prelude::*; use rcore_fs::vfs::{FileType, INode, Metadata}; use std::ffi::CString; @@ -13,10 +13,10 @@ use std::ffi::CString; pub fn load_exec_file_hdr_to_vec( file_path: &str, current_ref: &ThreadRef, -) -> Result<(Option, Arc, Vec, ElfHeader)> { - let (inode, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?; +) -> Result<(Option, FileRef, Vec, ElfHeader)> { + let (file_ref, file_buf, elf_hdr) = load_file_hdr_to_vec(&file_path, current_ref)?; if elf_hdr.is_some() { - Ok((None, inode, file_buf, elf_hdr.unwrap())) + Ok((None, file_ref, file_buf, elf_hdr.unwrap())) } else { // loaded file is not Elf format, try script file if !is_script_file(&file_buf) { @@ -30,14 +30,14 @@ pub fn load_exec_file_hdr_to_vec( "libos doesn't support executing binaries from \"/host\" directory" ); } - let (interp_inode, interp_buf, interp_hdr) = + let (interp_file, interp_buf, interp_hdr) = load_file_hdr_to_vec(&interpreter_path, current_ref)?; let interp_hdr = if interp_hdr.is_none() { return_errno!(ENOEXEC, "scrip interpreter is not ELF format"); } else { interp_hdr.unwrap() }; - Ok((Some(interpreter_path), interp_inode, interp_buf, interp_hdr)) + Ok((Some(interpreter_path), interp_file, interp_buf, interp_hdr)) } } @@ -72,13 +72,13 @@ fn parse_script_interpreter(file_buf: &Vec) -> Result { pub fn load_file_hdr_to_vec( file_path: &str, current_ref: &ThreadRef, -) -> Result<(Arc, Vec, Option)> { - let inode = current_ref +) -> Result<(FileRef, Vec, Option)> { + let file_ref = current_ref .fs() .read() .unwrap() - .lookup_inode(file_path) - .map_err(|e| errno!(e.errno(), "cannot find the file"))?; + .open_file(file_path, 0, FileMode::S_IRUSR)?; + let inode = file_ref.as_inode_file()?.inode(); // Make sure the final file to exec is not a directory let metadata = inode.metadata()?; @@ -105,12 +105,12 @@ pub fn load_file_hdr_to_vec( .read_elf64_lazy_as_vec() .map_err(|e| errno!(e.errno(), "failed to read the file"))?; - let elf_header = ElfFile::parse_elf_hdr(&inode, &mut file_buf); + let elf_header = ElfFile::parse_elf_hdr(&file_ref, &mut file_buf); if let Ok(elf_header) = elf_header { - Ok((inode, file_buf, Some(elf_header))) + Ok((file_ref, file_buf, Some(elf_header))) } else { // this file is not ELF format or there is something wrong when parsing warn!("parse elf header error = {}", elf_header.err().unwrap()); - Ok((inode, file_buf, None)) + Ok((file_ref, file_buf, None)) } } diff --git a/src/libos/src/process/do_spawn/mod.rs b/src/libos/src/process/do_spawn/mod.rs index 06cf4020..3a8ef6e9 100644 --- a/src/libos/src/process/do_spawn/mod.rs +++ b/src/libos/src/process/do_spawn/mod.rs @@ -166,7 +166,7 @@ fn new_process_common( parent_process: Option, ) -> Result { let mut argv = argv.clone().to_vec(); - let (is_script, elf_inode, mut elf_buf, elf_header) = + let (is_script, elf_file, mut elf_buf, elf_header) = load_exec_file_hdr_to_vec(file_path, current_ref)?; // elf_path might be different from file_path because file_path could lead to a script text file. @@ -182,13 +182,13 @@ fn new_process_common( file_path.to_string() }; - let exec_elf_hdr = ElfFile::new(&elf_inode, &mut elf_buf, elf_header) + let exec_elf_hdr = ElfFile::new(&elf_file, &mut elf_buf, elf_header) .cause_err(|e| errno!(e.errno(), "invalid executable"))?; let ldso_path = exec_elf_hdr .elf_interpreter() .ok_or_else(|| errno!(EINVAL, "cannot find the interpreter segment"))?; trace!("ldso_path = {:?}", ldso_path); - let (ldso_inode, mut ldso_elf_hdr_buf, ldso_elf_header) = + let (ldso_file, mut ldso_elf_hdr_buf, ldso_elf_header) = load_file_hdr_to_vec(ldso_path, current_ref) .cause_err(|e| errno!(e.errno(), "cannot load ld.so"))?; let ldso_elf_header = if ldso_elf_header.is_none() { @@ -196,7 +196,7 @@ fn new_process_common( } else { ldso_elf_header.unwrap() }; - let ldso_elf_hdr = ElfFile::new(&ldso_inode, &mut ldso_elf_hdr_buf, ldso_elf_header) + let ldso_elf_hdr = ElfFile::new(&ldso_file, &mut ldso_elf_hdr_buf, ldso_elf_header) .cause_err(|e| errno!(e.errno(), "invalid ld.so"))?; let new_process_ref = { diff --git a/src/libos/src/process/elf_file.rs b/src/libos/src/process/elf_file.rs index a6576c40..8b86cb34 100644 --- a/src/libos/src/process/elf_file.rs +++ b/src/libos/src/process/elf_file.rs @@ -15,7 +15,7 @@ const ELF64_HDR_SIZE: usize = 64; pub struct ElfFile<'a> { elf_buf: &'a [u8], elf_inner: Elf<'a>, - file_inode: &'a Arc, + file_ref: &'a FileRef, } impl<'a> Debug for ElfFile<'a> { @@ -30,7 +30,7 @@ impl<'a> Debug for ElfFile<'a> { impl<'a> ElfFile<'a> { pub fn new( - file_inode: &'a Arc, + file_ref: &'a FileRef, mut elf_buf: &'a mut [u8], header: ElfHeader, ) -> Result> { @@ -64,7 +64,7 @@ impl<'a> ElfFile<'a> { intepreter_offset, intepreter_count ); - file_inode.read_at( + file_ref.read_at( intepreter_offset, &mut elf_buf[intepreter_offset..intepreter_offset + intepreter_count], ); @@ -87,7 +87,7 @@ impl<'a> ElfFile<'a> { Ok(ElfFile { elf_buf, elf_inner, - file_inode, + file_ref, }) } @@ -107,11 +107,11 @@ impl<'a> ElfFile<'a> { self.elf_buf } - pub fn file_inode(&self) -> &Arc { - self.file_inode + pub fn file_ref(&self) -> &FileRef { + self.file_ref } - pub fn parse_elf_hdr(inode: &Arc, elf_buf: &mut Vec) -> Result { + pub fn parse_elf_hdr(elf_file: &FileRef, elf_buf: &mut Vec) -> Result { // TODO: Sanity check the number of program headers.. let mut phdr_start = 0; let mut phdr_end = 0; @@ -130,7 +130,7 @@ impl<'a> ElfFile<'a> { } let program_hdr_table_size = elf_hdr.e_phnum * elf_hdr.e_phentsize; - inode.read_at( + elf_file.read_at( elf_hdr.e_phoff as usize, &mut elf_buf[hdr_size..hdr_size + (program_hdr_table_size as usize)], )?; diff --git a/src/libos/src/vm/chunk.rs b/src/libos/src/vm/chunk.rs index 1d127fe0..2ee545c1 100644 --- a/src/libos/src/vm/chunk.rs +++ b/src/libos/src/vm/chunk.rs @@ -93,11 +93,10 @@ impl Chunk { } pub fn new_single_vma_chunk(vm_range: &VMRange, options: &VMMapOptions) -> Result { - let writeback_file = options.writeback_file().clone(); let vm_area = VMArea::new( vm_range.clone(), *options.perms(), - writeback_file, + options.initializer().backed_file(), DUMMY_CHUNK_PROCESS_ID, ); // Initialize the memory of the new range @@ -178,7 +177,7 @@ impl Chunk { if internal_manager.chunk_manager().free_size() < options.size() { return_errno!(ENOMEM, "no enough size without trying. try other chunks"); } - internal_manager.chunk_manager().mmap(options) + internal_manager.chunk_manager_mut().mmap(options) } pub fn is_single_vma(&self) -> bool { @@ -279,7 +278,11 @@ impl ChunkInternal { self.process_set.insert(pid); } - pub fn chunk_manager(&mut self) -> &mut ChunkManager { + pub fn chunk_manager(&self) -> &ChunkManager { + &self.chunk_manager + } + + pub fn chunk_manager_mut(&mut self) -> &mut ChunkManager { &mut self.chunk_manager } diff --git a/src/libos/src/vm/mod.rs b/src/libos/src/vm/mod.rs index 8752166d..3ce22b52 100644 --- a/src/libos/src/vm/mod.rs +++ b/src/libos/src/vm/mod.rs @@ -75,9 +75,10 @@ mod vm_util; use self::vm_layout::VMLayout; -pub use self::chunk::ChunkRef; +pub use self::chunk::{ChunkRef, ChunkType}; pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder}; pub use self::user_space_vm::USER_SPACE_VM_MANAGER; +pub use self::vm_area::VMArea; pub use self::vm_perms::VMPerms; pub use self::vm_range::VMRange; pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder}; diff --git a/src/libos/src/vm/process_vm.rs b/src/libos/src/vm/process_vm.rs index 7cb4810a..1126b4d6 100644 --- a/src/libos/src/vm/process_vm.rs +++ b/src/libos/src/vm/process_vm.rs @@ -7,7 +7,9 @@ use super::process::elf_file::{ElfFile, ProgramHeaderExt}; use super::user_space_vm::USER_SPACE_VM_MANAGER; use super::vm_area::VMArea; use super::vm_perms::VMPerms; -use super::vm_util::{VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions}; +use super::vm_util::{ + FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions, +}; use std::collections::HashSet; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -117,7 +119,9 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { .size(elf_layout.size()) .align(elf_layout.align()) .perms(VMPerms::ALL) // set it to read | write | exec for simplicity - .initializer(VMInitializer::DoNothing()) + .initializer(VMInitializer::ElfSpecific { + elf_file: elf_file.file_ref().clone(), + }) .build() .map_err(|e| { &self.handle_error_when_init(&chunks); @@ -244,7 +248,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> { } // Bytes of file_size length are loaded from the ELF file - elf_file.file_inode().read_at( + elf_file.file_ref().read_at( file_offset, &mut elf_proc_buf[mem_start_offset..mem_start_offset + file_size], ); @@ -318,6 +322,14 @@ impl ProcessVM { &self.mem_chunks } + pub fn stack_range(&self) -> &VMRange { + &self.stack_range + } + + pub fn heap_range(&self) -> &VMRange { + &self.heap_range + } + pub fn add_mem_chunk(&self, chunk: ChunkRef) { let mut mem_chunks = self.mem_chunks.write().unwrap(); mem_chunks.insert(chunk); @@ -488,28 +500,22 @@ impl ProcessVM { VMInitializer::DoNothing() } else { let file_ref = current!().file(fd)?; - VMInitializer::LoadFromFile { - file: file_ref, - offset: offset, + // Only shared, file-backed memory mappings have write-back files + let need_write_back = if flags.contains(MMapFlags::MAP_SHARED) { + true + } else { + false + }; + VMInitializer::FileBacked { + file: FileBacked::new(file_ref, offset, need_write_back), } } }; - // Only shared, file-backed memory mappings have write-back files - let writeback_file = if flags.contains(MMapFlags::MAP_SHARED) { - if let VMInitializer::LoadFromFile { file, offset } = &initializer { - Some((file.clone(), *offset)) - } else { - None - } - } else { - None - }; let mmap_options = VMMapOptionsBuilder::default() .size(size) .addr(addr_option) .perms(perms) .initializer(initializer) - .writeback_file(writeback_file) .build()?; let mmap_addr = USER_SPACE_VM_MANAGER.mmap(&mmap_options)?; Ok(mmap_addr) diff --git a/src/libos/src/vm/vm_area.rs b/src/libos/src/vm/vm_area.rs index c4a4dc23..f3a92114 100644 --- a/src/libos/src/vm/vm_area.rs +++ b/src/libos/src/vm/vm_area.rs @@ -2,6 +2,7 @@ use std::ops::{Deref, DerefMut}; use super::vm_perms::VMPerms; use super::vm_range::VMRange; +use super::vm_util::FileBacked; use super::*; use intrusive_collections::rbtree::{Link, RBTree}; @@ -11,7 +12,7 @@ use intrusive_collections::{intrusive_adapter, KeyAdapter}; pub struct VMArea { range: VMRange, perms: VMPerms, - writeback_file: Option<(FileRef, usize)>, + file_backed: Option, pid: pid_t, } @@ -19,13 +20,13 @@ impl VMArea { pub fn new( range: VMRange, perms: VMPerms, - writeback_file: Option<(FileRef, usize)>, + file_backed: Option, pid: pid_t, ) -> Self { Self { range, perms, - writeback_file, + file_backed, pid, } } @@ -38,20 +39,25 @@ impl VMArea { new_perms: VMPerms, pid: pid_t, ) -> Self { - let new_writeback_file = vma.writeback_file.as_ref().map(|(file, file_offset)| { - let new_file = file.clone(); + let new_backed_file = vma.file_backed.as_ref().map(|file| { + let mut new_file = file.clone(); + let file_offset = file.offset(); let new_file_offset = if vma.start() < new_range.start() { let vma_offset = new_range.start() - vma.start(); - *file_offset + vma_offset + file_offset + vma_offset } else { let vma_offset = vma.start() - new_range.start(); - debug_assert!(*file_offset >= vma_offset); - *file_offset - vma_offset + debug_assert!(file_offset >= vma_offset); + file_offset - vma_offset }; - (new_file, new_file_offset) + + new_file.set_offset(new_file_offset); + + new_file }); - Self::new(new_range, new_perms, new_writeback_file, pid) + + Self::new(new_range, new_perms, new_backed_file, pid) } pub fn perms(&self) -> VMPerms { @@ -66,8 +72,20 @@ impl VMArea { self.pid } - pub fn writeback_file(&self) -> &Option<(FileRef, usize)> { - &self.writeback_file + pub fn init_file(&self) -> Option<(&FileRef, usize)> { + if let Some(file) = &self.file_backed { + Some(file.init_file()) + } else { + None + } + } + + pub fn writeback_file(&self) -> Option<(&FileRef, usize)> { + if let Some(file) = &self.file_backed { + file.writeback_file() + } else { + None + } } pub fn set_perms(&mut self, new_perms: VMPerms) { @@ -103,15 +121,19 @@ impl VMArea { let old_start = self.start(); self.range.set_start(new_start); - // If the updates to the VMA needs to write back to a file, then the - // file offset must be adjusted according to the new start address. - if let Some((_, offset)) = self.writeback_file.as_mut() { + if let Some(file) = self.file_backed.as_mut() { + if !file.need_write_back() { + return; + } + // If the updates to the VMA needs to write back to a file, then the + // file offset must be adjusted according to the new start address. + let offset = file.offset(); if old_start < new_start { - *offset += new_start - old_start; + file.set_offset(offset + (new_start - old_start)); } else { // The caller must guarantee that the new start makes sense - debug_assert!(*offset >= old_start - new_start); - *offset -= old_start - new_start; + debug_assert!(offset >= old_start - new_start); + file.set_offset(offset - (old_start - new_start)); } } } @@ -223,22 +245,3 @@ impl VMAObj { &self.vma } } - -impl VMArea { - pub fn new_obj( - range: VMRange, - perms: VMPerms, - writeback_file: Option<(FileRef, usize)>, - pid: pid_t, - ) -> Box { - Box::new(VMAObj { - link: Link::new(), - vma: VMArea { - range, - perms, - writeback_file, - pid, - }, - }) - } -} diff --git a/src/libos/src/vm/vm_chunk_manager.rs b/src/libos/src/vm/vm_chunk_manager.rs index 7a89db76..ae404a39 100644 --- a/src/libos/src/vm/vm_chunk_manager.rs +++ b/src/libos/src/vm/vm_chunk_manager.rs @@ -116,9 +116,13 @@ impl ChunkManager { .free_manager .find_free_range_internal(size, align, addr)?; let new_addr = new_range.start(); - let writeback_file = options.writeback_file().clone(); let current_pid = current!().process().pid(); - let new_vma = VMArea::new(new_range, *options.perms(), writeback_file, current_pid); + let new_vma = VMArea::new( + new_range, + *options.perms(), + options.initializer().backed_file(), + current_pid, + ); // Initialize the memory of the new range let buf = unsafe { new_vma.as_slice_mut() }; @@ -416,7 +420,7 @@ impl ChunkManager { /// Same as flush_vma, except that an extra condition on the file needs to satisfy. pub fn flush_file_vma_with_cond bool>(vma: &VMArea, cond_fn: F) { - let (file, file_offset) = match vma.writeback_file().as_ref() { + let (file, file_offset) = match vma.writeback_file() { None => return, Some((file_and_offset)) => file_and_offset, }; @@ -430,7 +434,7 @@ impl ChunkManager { if !cond_fn(file) { return; } - file.write_at(*file_offset, unsafe { vma.as_slice() }); + file.write_at(file_offset, unsafe { vma.as_slice() }); } pub fn find_mmap_region(&self, addr: usize) -> Result { diff --git a/src/libos/src/vm/vm_manager.rs b/src/libos/src/vm/vm_manager.rs index 51951e9c..33084650 100644 --- a/src/libos/src/vm/vm_manager.rs +++ b/src/libos/src/vm/vm_manager.rs @@ -184,7 +184,11 @@ impl VMManager { "mmap with addr in existing default chunk: {:?}", chunk.range() ); - return chunk_internal.lock().unwrap().chunk_manager().mmap(options); + return chunk_internal + .lock() + .unwrap() + .chunk_manager_mut() + .mmap(options); } ChunkType::SingleVMA(_) => { match addr { @@ -287,7 +291,7 @@ impl VMManager { ChunkType::MultiVMA(manager) => manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .munmap_range(munmap_range)?, } } @@ -307,7 +311,7 @@ impl VMManager { return manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .munmap_range(munmap_range); } ChunkType::SingleVMA(_) => { @@ -353,7 +357,7 @@ impl VMManager { return manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .mprotect(addr, size, perms); } ChunkType::SingleVMA(_) => { @@ -383,7 +387,7 @@ impl VMManager { return manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .msync_by_range(&sync_range); } ChunkType::SingleVMA(vma) => { @@ -405,7 +409,7 @@ impl VMManager { manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .msync_by_file(sync_file); } ChunkType::SingleVMA(vma) => { @@ -452,7 +456,7 @@ impl VMManager { ChunkType::MultiVMA(manager) => manager .lock() .unwrap() - .chunk_manager() + .chunk_manager_mut() .parse_mremap_options(options), ChunkType::SingleVMA(vma) => { self.parse_mremap_options_for_single_vma_chunk(options, vma) diff --git a/src/libos/src/vm/vm_perms.rs b/src/libos/src/vm/vm_perms.rs index 3bd9e325..89e0fde7 100644 --- a/src/libos/src/vm/vm_perms.rs +++ b/src/libos/src/vm/vm_perms.rs @@ -55,6 +55,26 @@ impl VMPerms { assert!(sgx_status == sgx_status_t::SGX_SUCCESS && retval == 0); } } + + pub fn display(&self) -> String { + let mut str = String::new(); + if self.can_read() { + str += "r"; + } else { + str += "-"; + } + if self.can_write() { + str += "w"; + } else { + str += "-"; + } + if self.can_execute() { + str += "x"; + } else { + str += "-"; + } + str + } } impl Default for VMPerms { diff --git a/src/libos/src/vm/vm_util.rs b/src/libos/src/vm/vm_util.rs index be4a0e60..84599c67 100644 --- a/src/libos/src/vm/vm_util.rs +++ b/src/libos/src/vm/vm_util.rs @@ -1,9 +1,8 @@ use super::*; -// use super::vm_area::VMArea; -// use super::free_space_manager::VMFreeSpaceManager; use super::vm_area::*; use super::vm_perms::VMPerms; +use crate::fs::FileMode; use std::collections::BTreeSet; use intrusive_collections::rbtree::{Link, RBTree}; @@ -18,15 +17,19 @@ pub enum VMInitializer { CopyFrom { range: VMRange, }, - LoadFromFile { - file: FileRef, - offset: usize, + FileBacked { + file: FileBacked, + }, + // For ELF files, there is specical handling to not copy all the contents of the file. This is only used for tracking. + ElfSpecific { + elf_file: FileRef, }, // For file-backed mremap which may move from old range to new range and read extra bytes from file CopyOldAndReadNew { old_range: VMRange, file: FileRef, offset: usize, // read file from this offset + new_writeback_file: FileBacked, }, } @@ -39,7 +42,7 @@ impl Default for VMInitializer { impl VMInitializer { pub fn init_slice(&self, buf: &mut [u8]) -> Result<()> { match self { - VMInitializer::DoNothing() => { + VMInitializer::DoNothing() | VMInitializer::ElfSpecific { .. } => { // Do nothing } VMInitializer::FillZeros() => { @@ -55,10 +58,11 @@ impl VMInitializer { *b = 0; } } - VMInitializer::LoadFromFile { file, offset } => { + VMInitializer::FileBacked { file } => { // TODO: make sure that read_at does not move file cursor let len = file - .read_at(*offset, buf) + .file_ref() + .read_at(file.offset(), buf) .cause_err(|_| errno!(EACCES, "failed to init memory from file"))?; for b in &mut buf[len..] { *b = 0; @@ -68,6 +72,7 @@ impl VMInitializer { old_range, file, offset, + new_writeback_file, } => { // TODO: Handle old_range with non-readable subrange let src_slice = unsafe { old_range.as_slice() }; @@ -85,6 +90,66 @@ impl VMInitializer { } Ok(()) } + + pub fn backed_file(&self) -> Option { + match self { + VMInitializer::ElfSpecific { elf_file } => { + let file_ref = elf_file.clone(); + Some(FileBacked::new(file_ref, 0, false)) + } + VMInitializer::FileBacked { file } => Some(file.clone()), + VMInitializer::CopyOldAndReadNew { + new_writeback_file, .. + } => Some(new_writeback_file.clone()), + _ => None, + } + } +} + +// This struct is used to record file-backed memory type. +#[derive(Debug, Clone)] +pub struct FileBacked { + file: FileRef, + offset: usize, + write_back: bool, +} + +impl FileBacked { + pub fn new(file: FileRef, offset: usize, write_back: bool) -> Self { + Self { + file, + offset, + write_back, + } + } + + pub fn file_ref(&self) -> &FileRef { + &self.file + } + + pub fn offset(&self) -> usize { + self.offset + } + + pub fn set_offset(&mut self, offset: usize) { + self.offset = offset; + } + + pub fn need_write_back(&self) -> bool { + self.write_back + } + + pub fn init_file(&self) -> (&FileRef, usize) { + (&self.file, self.offset) + } + + pub fn writeback_file(&self) -> Option<(&FileRef, usize)> { + if self.write_back { + Some((&self.file, self.offset)) + } else { + None + } + } } #[derive(Clone, Copy, Debug, PartialEq)] @@ -118,8 +183,6 @@ pub struct VMMapOptions { perms: VMPerms, addr: VMMapAddr, initializer: VMInitializer, - // The content of the VMA can be written back to a given file at a given offset - writeback_file: Option<(FileRef, usize)>, } // VMMapOptionsBuilder is generated automatically, except the build function @@ -165,14 +228,12 @@ impl VMMapOptionsBuilder { Some(initializer) => initializer.clone(), None => VMInitializer::default(), }; - let writeback_file = self.writeback_file.take().unwrap_or_default(); Ok(VMMapOptions { size, align, perms, addr, initializer, - writeback_file, }) } } @@ -197,10 +258,6 @@ impl VMMapOptions { pub fn initializer(&self) -> &VMInitializer { &self.initializer } - - pub fn writeback_file(&self) -> &Option<(FileRef, usize)> { - &self.writeback_file - } } #[derive(Clone, Copy, PartialEq)] @@ -375,17 +432,18 @@ pub trait VMRemapParser { } (MRemapFlags::None, VMRemapSizeType::Growing, Some((backed_file, offset))) => { // Update writeback file offset - let new_writeback_file = Some((backed_file.clone(), offset + vma.size())); - let vm_initializer_for_new_range = VMInitializer::LoadFromFile { - file: backed_file.clone(), - offset: offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file + let vm_initializer_for_new_range = VMInitializer::FileBacked { + file: FileBacked::new( + backed_file.clone(), + offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file + true, + ), }; let mmap_opts = VMMapOptionsBuilder::default() .size(new_size - old_size) .addr(VMMapAddr::Need(old_range.end())) .perms(perms) .initializer(vm_initializer_for_new_range) - .writeback_file(new_writeback_file) .build()?; let ret_addr = Some(old_addr); (Some(mmap_opts), ret_addr) @@ -421,18 +479,18 @@ pub trait VMRemapParser { VMRange::new_with_size(old_addr + old_size, new_size - old_size)?; if self.is_free_range(&prefered_new_range) { // Don't need to move the old range - let vm_initializer_for_new_range = VMInitializer::LoadFromFile { - file: backed_file.clone(), - offset: offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file + let vm_initializer_for_new_range = VMInitializer::FileBacked { + file: FileBacked::new( + backed_file.clone(), + offset + vma.size(), // file-backed mremap should start from the end of previous mmap/mremap file + true, + ), }; - // Write back file should start from new offset - let new_writeback_file = Some((backed_file.clone(), offset + vma.size())); let mmap_ops = VMMapOptionsBuilder::default() .size(prefered_new_range.size()) .addr(VMMapAddr::Need(prefered_new_range.start())) .perms(perms) .initializer(vm_initializer_for_new_range) - .writeback_file(new_writeback_file) .build()?; (Some(mmap_ops), Some(old_addr)) } else { @@ -441,19 +499,19 @@ pub trait VMRemapParser { let copy_end = vma.end(); let copy_range = VMRange::new(old_range.start(), copy_end)?; let reread_file_start_offset = copy_end - vma.start(); + let new_writeback_file = FileBacked::new(backed_file.clone(), offset, true); VMInitializer::CopyOldAndReadNew { old_range: copy_range, file: backed_file.clone(), offset: reread_file_start_offset, + new_writeback_file: new_writeback_file, } }; - let new_writeback_file = Some((backed_file.clone(), *offset)); let mmap_ops = VMMapOptionsBuilder::default() .size(new_size) .addr(VMMapAddr::Any) .perms(perms) .initializer(vm_initializer_for_new_range) - .writeback_file(new_writeback_file) .build()?; // Cannot determine the returned address for now, which can only be obtained after calling mmap let ret_addr = None; @@ -476,19 +534,19 @@ pub trait VMRemapParser { let copy_end = vma.end(); let copy_range = VMRange::new(old_range.start(), copy_end)?; let reread_file_start_offset = copy_end - vma.start(); + let new_writeback_file = FileBacked::new(backed_file.clone(), offset, true); VMInitializer::CopyOldAndReadNew { old_range: copy_range, file: backed_file.clone(), offset: reread_file_start_offset, + new_writeback_file: new_writeback_file, } }; - let new_writeback_file = Some((backed_file.clone(), *offset)); let mmap_opts = VMMapOptionsBuilder::default() .size(new_size) .addr(VMMapAddr::Force(new_addr)) .perms(perms) .initializer(vm_initializer_for_new_range) - .writeback_file(new_writeback_file) .build()?; let ret_addr = Some(new_addr); (Some(mmap_opts), ret_addr) diff --git a/test/procfs/main.c b/test/procfs/main.c index b5af46ce..76ec01e8 100644 --- a/test/procfs/main.c +++ b/test/procfs/main.c @@ -44,6 +44,7 @@ static int test_read_from_procfs(const char *proc_inode) { } } while (len == sizeof(buf)); close(fd); + printf("test read from %s:\n%s\n", proc_inode, buf); return 0; } @@ -89,6 +90,15 @@ static int test_readlink_from_proc_self_cwd() { return 0; } +static int test_read_from_proc_self_maps() { + const char *proc_maps = "/proc/self/maps"; + + if (test_read_from_procfs(proc_maps) < 0) { + THROW_ERROR("failed to read the cpuinfo"); + } + return 0; +} + static int test_readlink_from_proc_self_root() { char root_buf[PATH_MAX] = { 0 }; const char *proc_root = "/proc/self/root"; @@ -282,6 +292,7 @@ static test_case_t test_cases[] = { TEST_CASE(test_readdir_root), TEST_CASE(test_readdir_self), TEST_CASE(test_readdir_self_fd), + TEST_CASE(test_read_from_proc_self_maps), }; int main(int argc, const char *argv[]) {