procfs: implement /proc/<PID>/maps

Implement the `maps` file which shows a process's VMA entries. Example
output:

```
[root@moss-machine /]# cat /proc/1/maps
500000000000-500000117000 r-xp 0000000000                    /bin/bash
50000012b000-500000130000 r--p 000011b000                    /bin/bash
500000130000-50000013e000 rw-p 0000120000                    /bin/bash
700000000000-70000002b000 r-xp 0000000000                    /lib/ld-linux-aarch64.so.1
70000003e000-700000040000 r--p 000002e000                    /lib/ld-linux-aarch64.so.1
700000040000-700000042000 rw-p 0000030000                    /lib/ld-linux-aarch64.so.1
7fffff510000-7fffff585000 r-xp 0000000000                    /usr/lib/libncursesw.so.6
7fffff585000-7fffff59b000 ---p 0000075000                    /usr/lib/libncursesw.so.6
7fffff59b000-7fffff5a0000 r--p 000007b000                    /usr/lib/libncursesw.so.6
7fffff5a0000-7fffff5a1000 rw-p 0000080000                    /usr/lib/libncursesw.so.6
7fffff5b0000-7fffff760000 r-xp 0000000000                    /usr/lib/libc.so.6
7fffff760000-7fffff76d000 ---p 00001b0000                    /usr/lib/libc.so.6
7fffff76d000-7fffff770000 r--p 00001bd000                    /usr/lib/libc.so.6
7fffff770000-7fffff772000 rw-p 00001c0000                    /usr/lib/libc.so.6
7fffff772000-7fffff779000 rw-p 0000000000
7fffff780000-7fffff7d9000 r-xp 0000000000                    /usr/lib/libreadline.so.8
7fffff7d9000-7fffff7ed000 ---p 0000059000                    /usr/lib/libreadline.so.8
7fffff7ed000-7fffff7f0000 r--p 000005d000                    /usr/lib/libreadline.so.8
7fffff7f0000-7fffff7f6000 rw-p 0000060000                    /usr/lib/libreadline.so.8
7fffff7f6000-7fffff7fb000 rw-p 0000000000
7fffff800000-800000063000 rw-p 0000000000                    [stack]
```
This commit is contained in:
Matthew Leach
2026-01-26 22:01:13 +00:00
parent d386b8ba36
commit 87fe041ba0
8 changed files with 119 additions and 16 deletions

View File

@@ -7,7 +7,7 @@ use crate::{
region::VirtMemoryRegion,
},
};
use alloc::{collections::BTreeMap, vec::Vec};
use alloc::{collections::BTreeMap, string::String, vec::Vec};
const MMAP_BASE: usize = 0x4000_0000_0000;
@@ -85,6 +85,7 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
mut len: usize,
perms: VMAPermissions,
kind: VMAreaKind,
name: String,
) -> Result<VA> {
if len == 0 {
return Err(KernelError::InvalidValue);
@@ -133,7 +134,9 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
// At this point, `start_addr` points to a valid, free region.
// We can now create and insert the new VMA, handling merges.
let new_vma = VMArea::new(region, kind, perms);
let mut new_vma = VMArea::new(region, kind, perms);
new_vma.set_name(name);
self.insert_and_merge(new_vma);
@@ -502,6 +505,10 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
pub fn vma_count(&self) -> usize {
self.vmas.len()
}
pub fn iter_vmas(&self) -> impl Iterator<Item = &VMArea> {
self.vmas.values()
}
}
#[cfg(test)]

View File

@@ -194,6 +194,7 @@ fn test_mmap_any_empty() {
size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -218,6 +219,7 @@ fn test_mmap_any_with_existing() {
size,
VMAPermissions::ro(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -231,6 +233,7 @@ fn test_mmap_any_with_existing() {
size,
VMAPermissions::ro(), // different permissions to prevent merge.
VMAreaKind::Anon,
String::new(),
)
.unwrap();
assert_eq!(bottom_addr.value(), existing_addr - size);
@@ -254,6 +257,7 @@ fn test_mmap_hint_free() {
size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -282,6 +286,7 @@ fn test_mmap_hint_taken() {
size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -309,6 +314,7 @@ fn test_mmap_fixed_clobber_complete_overlap() {
3 * PAGE_SIZE,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -345,6 +351,7 @@ fn test_mmap_fixed_clobber_partial_end() {
new_size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -378,6 +385,7 @@ fn test_mmap_fixed_clobber_partial_end_spill() {
new_size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();
@@ -413,6 +421,7 @@ fn test_mmap_fixed_no_clobber_fails() {
new_size,
VMAPermissions::rw(),
VMAreaKind::Anon,
String::new(),
)
.is_err()
);
@@ -438,6 +447,7 @@ fn test_mmap_fixed_clobber_punch_hole() {
new_size,
VMAPermissions::ro(),
VMAreaKind::Anon,
String::new(),
)
.unwrap();

View File

@@ -4,6 +4,7 @@ use crate::{
UserAddressSpace,
error::{KernelError, Result},
};
use alloc::string::ToString;
use memory_map::{AddressRequest, MemoryMap};
use vmarea::{AccessKind, FaultValidation, VMAPermissions, VMArea, VMAreaKind};
@@ -137,6 +138,7 @@ impl<AS: UserAddressSpace> ProcessVM<AS> {
growth_size,
BRK_PERMISSIONS,
VMAreaKind::Anon,
"[heap]".to_string(),
)?;
self.brk = new_brk_region;
@@ -174,6 +176,7 @@ mod tests {
region: VirtMemoryRegion::new(VA::from_value(0x1000), PAGE_SIZE),
kind: VMAreaKind::Anon, // Simplification for test
permissions: VMAPermissions::rx(),
name: String::new(),
};
ProcessVM::from_vma(text_vma).unwrap()
@@ -329,6 +332,7 @@ mod tests {
region: VirtMemoryRegion::new(obstacle_addr, PAGE_SIZE),
kind: VMAreaKind::Anon,
permissions: VMAPermissions::ro(),
name: String::new(),
};
vm.mm.insert_and_merge(obstacle_vma);
assert_eq!(vm.mm.vma_count(), 2);

View File

@@ -12,9 +12,10 @@
use core::cmp;
use crate::{
fs::Inode,
fs::{Inode, InodeId},
memory::{PAGE_MASK, PAGE_SIZE, address::VA, region::VirtMemoryRegion},
};
use alloc::string::{String, ToString};
use alloc::sync::Arc;
use object::{
Endian,
@@ -173,6 +174,7 @@ impl VMAreaKind {
#[derive(Clone, PartialEq)]
pub struct VMArea {
pub region: VirtMemoryRegion,
pub(super) name: String,
pub(super) kind: VMAreaKind,
pub(super) permissions: VMAPermissions,
}
@@ -189,9 +191,14 @@ impl VMArea {
region,
kind,
permissions,
name: String::new(),
}
}
pub fn set_name<S: AsRef<str>>(&mut self, s: S) {
self.name = s.as_ref().to_string();
}
/// Creates a file-backed `VMArea` directly from an ELF program header.
///
/// This is a convenience function used by the ELF loader. It parses the
@@ -246,6 +253,7 @@ impl VMArea {
len: hdr.p_filesz(endian) + mappable_region.offset() as u64,
}),
permissions,
name: String::new(),
}
}
@@ -455,6 +463,24 @@ impl VMArea {
pub fn region(&self) -> VirtMemoryRegion {
self.region
}
pub fn file_offset(&self) -> Option<u64> {
match self.kind {
VMAreaKind::File(ref vmfile_mapping) => Some(vmfile_mapping.offset()),
VMAreaKind::Anon => None,
}
}
pub fn inode_id(&self) -> Option<InodeId> {
match self.kind {
VMAreaKind::File(ref vmfile_mapping) => Some(vmfile_mapping.file().id()),
VMAreaKind::Anon => None,
}
}
pub fn name(&self) -> &str {
&self.name
}
}
#[cfg(test)]