Add entries other than `[stack]` in `/proc/[pid]/maps`

This commit is contained in:
Wang Siyuan 2026-01-22 03:49:15 +00:00 committed by Tate, Hongliang Tian
parent fb8c9e0c27
commit 08d54ec6ad
8 changed files with 121 additions and 28 deletions

View File

@ -9,7 +9,7 @@ use crate::{
utils::{Inode, mkmod}, utils::{Inode, mkmod},
}, },
prelude::*, prelude::*,
process::Process, process::{Process, posix_thread::AsPosixThread},
vm::vmar::{VMAR_CAP_ADDR, VMAR_LOWEST_ADDR}, vm::vmar::{VMAR_CAP_ADDR, VMAR_LOWEST_ADDR},
}; };
@ -36,16 +36,13 @@ impl FileOps for MapsFileOps {
return_errno_with_message!(Errno::ESRCH, "the process has exited"); return_errno_with_message!(Errno::ESRCH, "the process has exited");
}; };
let user_stack_top = vmar.process_vm().init_stack().user_stack_top(); let current = current_thread!();
let fs_ref = current.as_posix_thread().unwrap().read_fs();
let path_resolver = fs_ref.resolver().read();
let guard = vmar.query(VMAR_LOWEST_ADDR..VMAR_CAP_ADDR); let guard = vmar.query(VMAR_LOWEST_ADDR..VMAR_CAP_ADDR);
for vm_mapping in guard.iter() { for vm_mapping in guard.iter() {
if vm_mapping.map_to_addr() <= user_stack_top && vm_mapping.map_end() > user_stack_top { vm_mapping.print_to_maps(&mut printer, vmar, &path_resolver)?;
vm_mapping.print_to_maps(&mut printer, "[stack]")?;
} else {
// TODO: Print the status of mappings other than the stack.
continue;
}
} }
Ok(printer.bytes_written()) Ok(printer.bytes_written())

View File

@ -75,11 +75,11 @@ impl Heap {
Ok(()) Ok(())
} }
/// Returns the current heap end. /// Returns the current heap range.
pub fn heap_end(&self) -> Vaddr { pub fn heap_range(&self) -> Range<Vaddr> {
let inner = self.inner.lock(); let inner = self.inner.lock();
let inner = inner.as_ref().expect("Heap is not initialized"); let inner = inner.as_ref().expect("Heap is not initialized");
inner.heap_range.end inner.heap_range.clone()
} }
/// Modifies the end address of the heap. /// Modifies the end address of the heap.

View File

@ -385,6 +385,7 @@ fn map_segment_vmo(
let mut vm_map_options = vmar let mut vm_map_options = vmar
.new_map(segment_size, perms)? .new_map(segment_size, perms)?
.vmo(elf_vmo.clone()) .vmo(elf_vmo.clone())
.path(elf_file.clone())
.vmo_offset(segment_offset) .vmo_offset(segment_offset)
.can_overwrite(true); .can_overwrite(true);
vm_map_options = vm_map_options.offset(offset).handle_page_faults_around(); vm_map_options = vm_map_options.offset(offset).handle_page_faults_around();

View File

@ -18,7 +18,7 @@ pub fn sys_brk(heap_end: u64, ctx: &Context) -> Result<SyscallReturn> {
Some(addr) => user_heap Some(addr) => user_heap
.modify_heap_end(addr, ctx) .modify_heap_end(addr, ctx)
.unwrap_or_else(|cur_heap_end| cur_heap_end), .unwrap_or_else(|cur_heap_end| cur_heap_end),
None => user_heap.heap_end(), None => user_heap.heap_range().end,
}; };
Ok(SyscallReturn::Return(current_heap_end as _)) Ok(SyscallReturn::Return(current_heap_end as _))

View File

@ -113,7 +113,7 @@ fn do_sys_mmap(
options = options options = options
.may_perms(vm_may_perms) .may_perms(vm_may_perms)
.mappable(file.mappable()?) .mappable(file.as_ref().as_ref())?
.vmo_offset(offset) .vmo_offset(offset)
.handle_page_faults_around(); .handle_page_faults_around();
} }

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use alloc::{borrow::Cow, format};
use core::{ use core::{
cmp::{max, min}, cmp::{max, min},
num::NonZeroUsize, num::NonZeroUsize,
@ -17,9 +18,12 @@ use ostd::{
task::disable_preempt, task::disable_preempt,
}; };
use super::{RssType, interval_set::Interval, util::is_intersected, vmar_impls::RssDelta}; use super::{RssType, Vmar, interval_set::Interval, util::is_intersected, vmar_impls::RssDelta};
use crate::{ use crate::{
fs::utils::Inode, fs::{
path::{Path, PathResolver},
utils::Inode,
},
prelude::*, prelude::*,
thread::exception::PageFaultInfo, thread::exception::PageFaultInfo,
vm::{ vm::{
@ -69,6 +73,8 @@ pub struct VmMapping {
/// And the `mapped_mem` field must be the page cache of the inode, i.e. /// And the `mapped_mem` field must be the page cache of the inode, i.e.
/// [`MappedMemory::Vmo`]. /// [`MappedMemory::Vmo`].
inode: Option<Arc<dyn Inode>>, inode: Option<Arc<dyn Inode>>,
/// The path of the file that backs the mapping.
path: Option<Path>,
/// Whether the mapping is shared. /// Whether the mapping is shared.
/// ///
/// The updates to a shared mapping are visible among processes, or carried /// The updates to a shared mapping are visible among processes, or carried
@ -92,11 +98,13 @@ impl Interval<Vaddr> for VmMapping {
/***************************** Basic methods *********************************/ /***************************** Basic methods *********************************/
impl VmMapping { impl VmMapping {
#[expect(clippy::too_many_arguments)]
pub(super) fn new( pub(super) fn new(
map_size: NonZeroUsize, map_size: NonZeroUsize,
map_to_addr: Vaddr, map_to_addr: Vaddr,
mapped_mem: MappedMemory, mapped_mem: MappedMemory,
inode: Option<Arc<dyn Inode>>, inode: Option<Arc<dyn Inode>>,
path: Option<Path>,
is_shared: bool, is_shared: bool,
handle_page_faults_around: bool, handle_page_faults_around: bool,
perms: VmPerms, perms: VmPerms,
@ -106,6 +114,7 @@ impl VmMapping {
map_to_addr, map_to_addr,
mapped_mem, mapped_mem,
inode, inode,
path,
is_shared, is_shared,
handle_page_faults_around, handle_page_faults_around,
perms, perms,
@ -116,6 +125,7 @@ impl VmMapping {
VmMapping { VmMapping {
mapped_mem: self.mapped_mem.dup(), mapped_mem: self.mapped_mem.dup(),
inode: self.inode.clone(), inode: self.inode.clone(),
path: self.path.clone(),
..*self ..*self
} }
} }
@ -205,7 +215,12 @@ impl VmMapping {
/// Prints the mapping information in the format of `/proc/[pid]/maps`. /// Prints the mapping information in the format of `/proc/[pid]/maps`.
/// ///
/// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/task_mmu.c#L304-L359> /// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/task_mmu.c#L304-L359>
pub fn print_to_maps(&self, printer: &mut VmPrinter, name: &str) -> Result<()> { pub fn print_to_maps(
&self,
printer: &mut VmPrinter,
parent_vmar: &Vmar,
path_resolver: &PathResolver,
) -> Result<()> {
let start = self.map_to_addr; let start = self.map_to_addr;
let end = self.map_end(); let end = self.map_end();
let read_char = if self.perms.contains(VmPerms::READ) { let read_char = if self.perms.contains(VmPerms::READ) {
@ -231,9 +246,8 @@ impl VmMapping {
.unwrap_or((0, 0)); .unwrap_or((0, 0));
let ino = self.inode().map(|inode| inode.ino()).unwrap_or(0); let ino = self.inode().map(|inode| inode.ino()).unwrap_or(0);
writeln!( let line = format!(
printer, "{:x}-{:x} {}{}{}{} {:08x} {:02x}:{:02x} {} ",
"{:x}-{:x} {}{}{}{} {:08x} {:02x}:{:02x} {:<26} {}",
start, start,
end, end,
read_char, read_char,
@ -244,8 +258,57 @@ impl VmMapping {
dev_major, dev_major,
dev_minor, dev_minor,
ino, ino,
name );
)?;
let name = || {
let process_vm = parent_vmar.process_vm();
let user_stack_top = process_vm.init_stack().user_stack_top();
if self.map_to_addr <= user_stack_top && self.map_end() > user_stack_top {
return Some(Cow::Borrowed("[stack]"));
}
let heap_range = process_vm.heap().heap_range();
if self.map_to_addr >= heap_range.start && self.map_end() <= heap_range.end {
return Some(Cow::Borrowed("[heap]"));
}
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
if let Some(vmo) = self.vmo() {
use crate::vdso::{VDSO_VMO_LAYOUT, vdso_vmo};
if let Some(vdso_vmo) = vdso_vmo()
&& Arc::ptr_eq(vmo.vmo(), &vdso_vmo)
{
let offset = vmo.offset();
if offset == VDSO_VMO_LAYOUT.data_segment_offset {
return Some(Cow::Borrowed("[vvar]"));
} else if offset == VDSO_VMO_LAYOUT.text_segment_offset {
return Some(Cow::Borrowed("[vdso]"));
}
}
}
if let Some(path) = &self.path {
return Some(Cow::Owned(path_resolver.make_abs_path(path).into_string()));
}
// Reference: <https://github.com/google/gvisor/blob/38123b53da96ff6983fcc103dfe2a9cc4e0d80c8/test/syscalls/linux/proc.cc#L1158-L1172>
if matches!(&self.mapped_mem, MappedMemory::Vmo(_)) && self.is_shared {
return Some(Cow::Borrowed("/dev/zero (deleted)"));
}
// Common anonymous mappings do not have names.
None
};
let name = name();
if let Some(name) = name {
writeln!(printer, "{:<72} {}", line, name)?;
} else {
writeln!(printer, "{}", line)?;
}
Ok(()) Ok(())
} }
@ -578,6 +641,7 @@ impl VmMapping {
map_size: NonZeroUsize::new(left_size).unwrap(), map_size: NonZeroUsize::new(left_size).unwrap(),
mapped_mem: l_mapped_mem, mapped_mem: l_mapped_mem,
inode: self.inode.clone(), inode: self.inode.clone(),
path: self.path.clone(),
..self ..self
}; };
let right = Self { let right = Self {
@ -900,6 +964,7 @@ fn try_merge(left: &VmMapping, right: &VmMapping) -> Option<VmMapping> {
map_size, map_size,
mapped_mem, mapped_mem,
inode: left.inode.clone(), inode: left.inode.clone(),
path: left.path.clone(),
..*left ..*left
}) })
} }

View File

@ -4,7 +4,11 @@ use core::num::NonZeroUsize;
use super::{MappedMemory, MappedVmo, RssDelta, VmMapping, Vmar}; use super::{MappedMemory, MappedVmo, RssDelta, VmMapping, Vmar};
use crate::{ use crate::{
fs::{file_handle::Mappable, ramfs::memfd::MemfdInode}, fs::{
file_handle::{FileLike, Mappable},
path::Path,
ramfs::memfd::MemfdInode,
},
prelude::*, prelude::*,
vm::{perms::VmPerms, vmo::Vmo}, vm::{perms::VmPerms, vmo::Vmo},
}; };
@ -50,6 +54,7 @@ pub struct VmarMapOptions<'a> {
parent: &'a Vmar, parent: &'a Vmar,
vmo: Option<Arc<Vmo>>, vmo: Option<Arc<Vmo>>,
mappable: Option<Mappable>, mappable: Option<Mappable>,
path: Option<Path>,
perms: VmPerms, perms: VmPerms,
may_perms: VmPerms, may_perms: VmPerms,
vmo_offset: usize, vmo_offset: usize,
@ -71,6 +76,7 @@ impl<'a> VmarMapOptions<'a> {
parent, parent,
vmo: None, vmo: None,
mappable: None, mappable: None,
path: None,
perms, perms,
may_perms: VmPerms::ALL_MAY_PERMS, may_perms: VmPerms::ALL_MAY_PERMS,
vmo_offset: 0, vmo_offset: 0,
@ -124,6 +130,19 @@ impl<'a> VmarMapOptions<'a> {
self self
} }
/// Sets the [`Path`] of the mapping.
///
/// # Panics
///
/// This function panics if a [`Mappable`] is already provided.
pub fn path(mut self, path: Path) -> Self {
if self.mappable.is_some() {
panic!("Cannot set `path` when `mappable` is already set");
}
self.path = Some(path);
self
}
/// Sets the offset of the first memory page in the VMO that is to be /// Sets the offset of the first memory page in the VMO that is to be
/// mapped into the VMAR. /// mapped into the VMAR.
/// ///
@ -194,23 +213,34 @@ impl<'a> VmarMapOptions<'a> {
/// ///
/// # Panics /// # Panics
/// ///
/// This function panics if a [`Vmo`] or [`Mappable`] is already provided. /// This function panics if a [`Vmo`], [`Path`] or [`Mappable`] is already provided.
pub fn mappable(mut self, mappable: Mappable) -> Self { ///
/// # Errors
///
/// This function returns an error if the file does not have a corresponding
/// mappable object of [`crate::fs::file_handle::Mappable`].
pub fn mappable(mut self, file: &dyn FileLike) -> Result<Self> {
if self.vmo.is_some() { if self.vmo.is_some() {
panic!("Cannot set `mappable` when `vmo` is already set"); panic!("Cannot set `mappable` when `vmo` is already set");
} }
if self.path.is_some() {
panic!("Cannot set `mappable` when `path` is already set");
}
if self.mappable.is_some() { if self.mappable.is_some() {
panic!("Cannot set `mappable` when `mappable` is already set"); panic!("Cannot set `mappable` when `mappable` is already set");
} }
let mappable = file.mappable()?;
// Verify whether the page cache inode is valid. // Verify whether the page cache inode is valid.
if let Mappable::Inode(ref inode) = mappable { if let Mappable::Inode(ref inode) = mappable {
self.vmo = Some(inode.page_cache().expect("Map an inode without page cache")); self.vmo = Some(inode.page_cache().expect("Map an inode without page cache"));
} }
self.mappable = Some(mappable); self.mappable = Some(mappable);
self.path = Some(file.path().clone());
self Ok(self)
} }
/// Creates the mapping and adds it to the parent VMAR. /// Creates the mapping and adds it to the parent VMAR.
@ -224,6 +254,7 @@ impl<'a> VmarMapOptions<'a> {
parent, parent,
vmo, vmo,
mappable, mappable,
path,
perms, perms,
mut may_perms, mut may_perms,
vmo_offset, vmo_offset,
@ -321,6 +352,7 @@ impl<'a> VmarMapOptions<'a> {
map_to_addr, map_to_addr,
mapped_mem, mapped_mem,
inode, inode,
path,
is_shared, is_shared,
handle_page_faults_around, handle_page_faults_around,
perms | may_perms, perms | may_perms,

View File

@ -28,11 +28,9 @@ ProcSelfCwd.Absolute
ProcSelfFd.OpenFd ProcSelfFd.OpenFd
# TODO: Support `O_LARGEFILE` flag. # TODO: Support `O_LARGEFILE` flag.
ProcSelfFdInfo.Flags ProcSelfFdInfo.Flags
ProcSelfMaps.Basic # TODO: Mappings created with only `PROT_WRITE` should be shown as `-w-`.
ProcSelfMaps.Map1
ProcSelfMaps.Map2 ProcSelfMaps.Map2
ProcSelfMaps.MapUnmap ProcSelfMaps.MapUnmap
ProcSelfMaps.SharedAnon
ProcSelfMounts.ContainsProcfsEntry ProcSelfMounts.ContainsProcfsEntry
ProcSelfMounts.RequiredFieldsArePresent ProcSelfMounts.RequiredFieldsArePresent
ProcSelfRoot.IsRoot ProcSelfRoot.IsRoot