mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-01-31 01:21:46 -05:00
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:
@@ -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)]
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -126,12 +126,18 @@ impl Inode for ProcTaskInode {
|
||||
FileType::Directory,
|
||||
7,
|
||||
));
|
||||
entries.push(Dirent::new(
|
||||
"maps".to_string(),
|
||||
InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "maps"])),
|
||||
FileType::File,
|
||||
8,
|
||||
));
|
||||
if self.desc.tid().value() == self.desc.tgid().value() && !self.is_task_dir {
|
||||
entries.push(Dirent::new(
|
||||
"task".to_string(),
|
||||
InodeId::from_fsid_and_inodeid(PROCFS_ID, get_inode_id(&[&initial_str, "task"])),
|
||||
FileType::Directory,
|
||||
8,
|
||||
9,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ pub enum TaskFileType {
|
||||
Root,
|
||||
State,
|
||||
Stat,
|
||||
Maps,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for TaskFileType {
|
||||
@@ -30,6 +31,7 @@ impl TryFrom<&str> for TaskFileType {
|
||||
"stat" => Ok(TaskFileType::Stat),
|
||||
"cwd" => Ok(TaskFileType::Cwd),
|
||||
"root" => Ok(TaskFileType::Root),
|
||||
"maps" => Ok(TaskFileType::Maps),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
@@ -52,6 +54,7 @@ impl ProcTaskFileInode {
|
||||
TaskFileType::Status
|
||||
| TaskFileType::Comm
|
||||
| TaskFileType::State
|
||||
| TaskFileType::Maps
|
||||
| TaskFileType::Stat => FileType::File,
|
||||
TaskFileType::Cwd | TaskFileType::Root => FileType::Symlink,
|
||||
},
|
||||
@@ -174,6 +177,26 @@ Threads:\t{tasks}\n",
|
||||
}
|
||||
TaskFileType::Cwd => task.cwd.lock_save_irq().clone().1.as_str().to_string(),
|
||||
TaskFileType::Root => task.root.lock_save_irq().1.as_str().to_string(),
|
||||
TaskFileType::Maps => {
|
||||
let mut output = String::new();
|
||||
let mut vm = task.vm.lock_save_irq();
|
||||
|
||||
for vma in vm.mm_mut().iter_vmas() {
|
||||
output.push_str(&format!(
|
||||
"{:x}-{:x} {}{}{}{} {:010x} {}\n",
|
||||
vma.region().start_address().value(),
|
||||
vma.region().end_address().value(),
|
||||
if vma.permissions().read { "r" } else { "-" },
|
||||
if vma.permissions().write { "w" } else { "-" },
|
||||
if vma.permissions().execute { "x" } else { "-" },
|
||||
"p", // Don't suport shared mappings... yet!
|
||||
vma.file_offset().unwrap_or_default(),
|
||||
vma.name()
|
||||
));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
} else {
|
||||
"State:\tGone\n".to_string()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::{process::fd_table::Fd, sched::current::current_task};
|
||||
use alloc::string::{String, ToString};
|
||||
use libkernel::{
|
||||
error::{KernelError, Result},
|
||||
memory::{
|
||||
@@ -85,8 +86,8 @@ pub async fn sys_mmap(
|
||||
|
||||
let requested_len = len as usize;
|
||||
|
||||
let kind = if (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0 {
|
||||
VMAreaKind::Anon
|
||||
let (kind, name) = if (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0 {
|
||||
(VMAreaKind::Anon, String::new())
|
||||
} else {
|
||||
// File-backed mapping: require a valid fd and use the provided offset.
|
||||
let fd = current_task()
|
||||
@@ -96,8 +97,12 @@ pub async fn sys_mmap(
|
||||
.ok_or(KernelError::BadFd)?;
|
||||
|
||||
let inode = fd.inode().ok_or(KernelError::BadFd)?;
|
||||
let name = fd
|
||||
.path()
|
||||
.map(|x| x.as_str().to_string())
|
||||
.unwrap_or_default();
|
||||
|
||||
VMAreaKind::new_file(inode, offset, len)
|
||||
(VMAreaKind::new_file(inode, offset, len), name)
|
||||
};
|
||||
|
||||
let address_request = if addr.is_null() {
|
||||
@@ -125,6 +130,7 @@ pub async fn sys_mmap(
|
||||
requested_len,
|
||||
permissions,
|
||||
kind,
|
||||
name,
|
||||
)?;
|
||||
|
||||
Ok(new_mapping_addr.value())
|
||||
|
||||
@@ -59,13 +59,14 @@ fn process_prog_headers<E: Endian>(
|
||||
vmas: &mut Vec<VMArea>,
|
||||
bias: Option<usize>,
|
||||
elf_file: Arc<dyn Inode>,
|
||||
path: &Path,
|
||||
endian: E,
|
||||
) -> Option<VA> {
|
||||
let mut hdr_addr = None;
|
||||
|
||||
for hdr in hdrs {
|
||||
if hdr.p_type(endian) == PT_LOAD {
|
||||
let vma = VMArea::from_pheader(elf_file.clone(), *hdr, endian, bias);
|
||||
let mut vma = VMArea::from_pheader(elf_file.clone(), *hdr, endian, bias);
|
||||
|
||||
// Find PHDR: Assumption segment with p_offset == 0 contains
|
||||
// headers.
|
||||
@@ -73,6 +74,8 @@ fn process_prog_headers<E: Endian>(
|
||||
hdr_addr = Some(vma.region().start_address());
|
||||
}
|
||||
|
||||
vma.set_name(path.as_str());
|
||||
|
||||
vmas.push(vma);
|
||||
}
|
||||
}
|
||||
@@ -80,7 +83,12 @@ fn process_prog_headers<E: Endian>(
|
||||
hdr_addr
|
||||
}
|
||||
|
||||
async fn exec_elf(inode: Arc<dyn Inode>, argv: Vec<String>, envp: Vec<String>) -> Result<()> {
|
||||
async fn exec_elf(
|
||||
inode: Arc<dyn Inode>,
|
||||
path: &Path,
|
||||
argv: Vec<String>,
|
||||
envp: Vec<String>,
|
||||
) -> Result<()> {
|
||||
// Read ELF header
|
||||
let mut buf = [0u8; core::mem::size_of::<elf::FileHeader64<LittleEndian>>()];
|
||||
inode.read_at(0, &mut buf).await?;
|
||||
@@ -137,7 +145,8 @@ async fn exec_elf(inode: Arc<dyn Inode>, argv: Vec<String>, envp: Vec<String>) -
|
||||
let mut vmas = Vec::new();
|
||||
|
||||
// Process the binary program headers.
|
||||
if let Some(hdr_addr) = process_prog_headers(hdrs, &mut vmas, main_bias, inode.clone(), endian)
|
||||
if let Some(hdr_addr) =
|
||||
process_prog_headers(hdrs, &mut vmas, main_bias, inode.clone(), path, endian)
|
||||
{
|
||||
auxv.push(AT_PHDR);
|
||||
auxv.push(hdr_addr.add_bytes(elf.e_phoff(endian) as _).value() as _);
|
||||
@@ -160,11 +169,15 @@ async fn exec_elf(inode: Arc<dyn Inode>, argv: Vec<String>, envp: Vec<String>) -
|
||||
main_entry
|
||||
};
|
||||
|
||||
vmas.push(VMArea::new(
|
||||
let mut stack_vma = VMArea::new(
|
||||
VirtMemoryRegion::new(VA::from_value(STACK_START), STACK_SZ),
|
||||
VMAreaKind::Anon,
|
||||
VMAPermissions::rw(),
|
||||
));
|
||||
);
|
||||
|
||||
stack_vma.set_name("[stack]");
|
||||
|
||||
vmas.push(stack_vma);
|
||||
|
||||
let mut mem_map = MemoryMap::from_vmas(vmas)?;
|
||||
let stack_ptr = setup_user_stack(&mut mem_map, &argv, &envp, auxv)?;
|
||||
@@ -231,12 +244,13 @@ async fn exec_script(
|
||||
new_argv.push(path.as_str().to_string());
|
||||
new_argv.extend(argv.into_iter().skip(1)); // Skip original argv[0]
|
||||
// Resolve interpreter inode
|
||||
let interp_path = Path::new(interp_path);
|
||||
let task = current_task_shared();
|
||||
let interp_inode = VFS
|
||||
.resolve_path(Path::new(interp_path), VFS.root_inode(), &task)
|
||||
.resolve_path(interp_path, VFS.root_inode(), &task)
|
||||
.await?;
|
||||
// Execute interpreter
|
||||
exec_elf(interp_inode, new_argv, envp).await?;
|
||||
exec_elf(interp_inode, interp_path, new_argv, envp).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -249,7 +263,7 @@ pub async fn kernel_exec(
|
||||
let mut buf = [0u8; 4];
|
||||
inode.read_at(0, &mut buf).await?;
|
||||
if buf == [0x7F, b'E', b'L', b'F'] {
|
||||
exec_elf(inode, argv, envp).await
|
||||
exec_elf(inode, path, argv, envp).await
|
||||
} else if buf.starts_with(b"#!") {
|
||||
exec_script(path, inode, argv, envp).await
|
||||
} else {
|
||||
@@ -394,7 +408,14 @@ async fn process_interp(interp_path: String, vmas: &mut Vec<VMArea>) -> Result<V
|
||||
.map_err(|_| ExecError::InvalidPHdrFormat)?;
|
||||
|
||||
// Build VMAs for interpreter
|
||||
process_prog_headers(interp_hdrs, vmas, Some(LINKER_BIAS), interp_inode, iendian);
|
||||
process_prog_headers(
|
||||
interp_hdrs,
|
||||
vmas,
|
||||
Some(LINKER_BIAS),
|
||||
interp_inode,
|
||||
path,
|
||||
iendian,
|
||||
);
|
||||
|
||||
let interp_entry = VA::from_value(LINKER_BIAS + interp_elf.e_entry(iendian) as usize);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user