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},
},
prelude::*,
process::Process,
process::{Process, posix_thread::AsPosixThread},
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");
};
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);
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, "[stack]")?;
} else {
// TODO: Print the status of mappings other than the stack.
continue;
}
vm_mapping.print_to_maps(&mut printer, vmar, &path_resolver)?;
}
Ok(printer.bytes_written())

View File

@ -75,11 +75,11 @@ impl Heap {
Ok(())
}
/// Returns the current heap end.
pub fn heap_end(&self) -> Vaddr {
/// Returns the current heap range.
pub fn heap_range(&self) -> Range<Vaddr> {
let inner = self.inner.lock();
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.

View File

@ -385,6 +385,7 @@ fn map_segment_vmo(
let mut vm_map_options = vmar
.new_map(segment_size, perms)?
.vmo(elf_vmo.clone())
.path(elf_file.clone())
.vmo_offset(segment_offset)
.can_overwrite(true);
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
.modify_heap_end(addr, ctx)
.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 _))

View File

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

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{borrow::Cow, format};
use core::{
cmp::{max, min},
num::NonZeroUsize,
@ -17,9 +18,12 @@ use ostd::{
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::{
fs::utils::Inode,
fs::{
path::{Path, PathResolver},
utils::Inode,
},
prelude::*,
thread::exception::PageFaultInfo,
vm::{
@ -69,6 +73,8 @@ pub struct VmMapping {
/// And the `mapped_mem` field must be the page cache of the inode, i.e.
/// [`MappedMemory::Vmo`].
inode: Option<Arc<dyn Inode>>,
/// The path of the file that backs the mapping.
path: Option<Path>,
/// Whether the mapping is shared.
///
/// The updates to a shared mapping are visible among processes, or carried
@ -92,11 +98,13 @@ impl Interval<Vaddr> for VmMapping {
/***************************** Basic methods *********************************/
impl VmMapping {
#[expect(clippy::too_many_arguments)]
pub(super) fn new(
map_size: NonZeroUsize,
map_to_addr: Vaddr,
mapped_mem: MappedMemory,
inode: Option<Arc<dyn Inode>>,
path: Option<Path>,
is_shared: bool,
handle_page_faults_around: bool,
perms: VmPerms,
@ -106,6 +114,7 @@ impl VmMapping {
map_to_addr,
mapped_mem,
inode,
path,
is_shared,
handle_page_faults_around,
perms,
@ -116,6 +125,7 @@ impl VmMapping {
VmMapping {
mapped_mem: self.mapped_mem.dup(),
inode: self.inode.clone(),
path: self.path.clone(),
..*self
}
}
@ -205,7 +215,12 @@ impl VmMapping {
/// 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>
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 end = self.map_end();
let read_char = if self.perms.contains(VmPerms::READ) {
@ -231,9 +246,8 @@ impl VmMapping {
.unwrap_or((0, 0));
let ino = self.inode().map(|inode| inode.ino()).unwrap_or(0);
writeln!(
printer,
"{:x}-{:x} {}{}{}{} {:08x} {:02x}:{:02x} {:<26} {}",
let line = format!(
"{:x}-{:x} {}{}{}{} {:08x} {:02x}:{:02x} {} ",
start,
end,
read_char,
@ -244,8 +258,57 @@ impl VmMapping {
dev_major,
dev_minor,
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(())
}
@ -578,6 +641,7 @@ impl VmMapping {
map_size: NonZeroUsize::new(left_size).unwrap(),
mapped_mem: l_mapped_mem,
inode: self.inode.clone(),
path: self.path.clone(),
..self
};
let right = Self {
@ -900,6 +964,7 @@ fn try_merge(left: &VmMapping, right: &VmMapping) -> Option<VmMapping> {
map_size,
mapped_mem,
inode: left.inode.clone(),
path: left.path.clone(),
..*left
})
}

View File

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

View File

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