Module rsyscall.sys.mman
#include <sys/mman.h>
Expand source code Browse git
"`#include <sys/mman.h>`"
from __future__ import annotations
from rsyscall._raw import lib # type: ignore
from rsyscall.linux.memfd import MFD
import enum
__all__ = [
"MFD",
"PROT",
"MAP",
"MappableFileDescriptor",
"MemoryMappingTask",
"MemoryMapping",
]
class PROT(enum.IntFlag):
EXEC = lib.PROT_EXEC
READ = lib.PROT_READ
WRITE = lib.PROT_WRITE
NONE = lib.PROT_NONE
class MAP(enum.IntFlag):
PRIVATE = lib.MAP_PRIVATE
SHARED = lib.MAP_SHARED
ANONYMOUS = lib.MAP_ANONYMOUS
POPULATE = lib.MAP_POPULATE
#### Classes ####
from dataclasses import dataclass
import rsyscall.far
import rsyscall.near
import rsyscall.near as near
import typing as t
@dataclass(frozen=True)
class MemoryMapping:
__slots__ = ('task', 'near', 'file')
task: MemoryMappingTask
near: rsyscall.near.MemoryMapping
file: rsyscall.far.File
async def munmap(self) -> None:
await _munmap(self.task.sysif, self.near)
def for_task(self, task: MemoryMappingTask) -> MemoryMapping:
if task.address_space != self.task.address_space:
raise rsyscall.far.AddressSpaceMismatchError()
return MemoryMapping(task, self.near, self.file)
def __str__(self) -> str:
return f"MemoryMapping({str(self.task)}, {str(self.near)})"
def __repr__(self) -> str:
return str(self)
from rsyscall.handle.fd import BaseFileDescriptor, FileDescriptorTask
class MemoryMappingTask(FileDescriptorTask):
async def mmap(self, length: int, prot: PROT, flags: MAP,
page_size: int=4096,
) -> MemoryMapping:
# a mapping without a file descriptor, is an anonymous mapping
flags |= MAP.ANONYMOUS
ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size)
return MemoryMapping(self, ret, rsyscall.far.File())
class MappableFileDescriptor(BaseFileDescriptor):
task: MemoryMappingTask
async def mmap(self, length: int, prot: PROT, flags: MAP,
offset: int=0,
page_size: int=4096,
file: rsyscall.far.File=None,
) -> MemoryMapping:
self._validate()
if file is None:
file = rsyscall.far.File()
ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size)
return MemoryMapping(self.task, ret, file)
#### Raw syscalls ####
from rsyscall.near.sysif import SyscallInterface
from rsyscall.sys.syscall import SYS
async def _mmap(sysif: SyscallInterface, length: int, prot: PROT, flags: MAP,
addr: t.Optional[near.Address]=None,
fd: t.Optional[near.FileDescriptor]=None, offset: int=0,
page_size: int=4096) -> near.MemoryMapping:
if addr is None:
addr = 0 # type: ignore
else:
assert (int(addr) % page_size) == 0
if fd is None:
fd = -1 # type: ignore
# TODO we want Linux to enforce this for us, but instead it just rounds,
# leaving us unable to later munmap.
assert (int(length) % page_size) == 0
ret = await sysif.syscall(SYS.mmap, addr, length, prot, flags, fd, offset)
return near.MemoryMapping(address=ret, length=length, page_size=page_size)
async def _munmap(sysif: SyscallInterface, mapping: near.MemoryMapping) -> None:
await sysif.syscall(SYS.munmap, mapping.address, mapping.length)
Classes
class MFD (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code Browse git
class MFD(enum.IntFlag): NONE = 0 CLOEXEC = lib.MFD_CLOEXEC ALLOW_SEALING = lib.MFD_ALLOW_SEALING HUGETLB = lib.MFD_HUGETLB HUGE_2MB = lib.MFD_HUGE_2MB HUGE_1GB = lib.MFD_HUGE_1GB
Ancestors
- enum.IntFlag
- builtins.int
- enum.Flag
- enum.Enum
Class variables
var NONE
var CLOEXEC
var ALLOW_SEALING
var HUGETLB
var HUGE_2MB
var HUGE_1GB
class PROT (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code Browse git
class PROT(enum.IntFlag): EXEC = lib.PROT_EXEC READ = lib.PROT_READ WRITE = lib.PROT_WRITE NONE = lib.PROT_NONE
Ancestors
- enum.IntFlag
- builtins.int
- enum.Flag
- enum.Enum
Class variables
var EXEC
var READ
var WRITE
var NONE
class MAP (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
An enumeration.
Expand source code Browse git
class MAP(enum.IntFlag): PRIVATE = lib.MAP_PRIVATE SHARED = lib.MAP_SHARED ANONYMOUS = lib.MAP_ANONYMOUS POPULATE = lib.MAP_POPULATE
Ancestors
- enum.IntFlag
- builtins.int
- enum.Flag
- enum.Enum
Class variables
var PRIVATE
var SHARED
var ANONYMOUS
var POPULATE
class MappableFileDescriptor (task: FileDescriptorTask, near: FileDescriptor, valid: bool)
-
A file descriptor accessed through some
Task
This is an rsyscall-internal base class, which other
FileDescriptor
objects inherit from for core lifecycle methods. SeeFileDescriptor
for more information.Expand source code Browse git
class MappableFileDescriptor(BaseFileDescriptor): task: MemoryMappingTask async def mmap(self, length: int, prot: PROT, flags: MAP, offset: int=0, page_size: int=4096, file: rsyscall.far.File=None, ) -> MemoryMapping: self._validate() if file is None: file = rsyscall.far.File() ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size) return MemoryMapping(self.task, ret, file)
Ancestors
Subclasses
Methods
async def mmap(self, length: int, prot: PROT, flags: MAP, offset: int = 0, page_size: int = 4096, file: File = None) ‑> MemoryMapping
-
Expand source code Browse git
async def mmap(self, length: int, prot: PROT, flags: MAP, offset: int=0, page_size: int=4096, file: rsyscall.far.File=None, ) -> MemoryMapping: self._validate() if file is None: file = rsyscall.far.File() ret = await _mmap(self.task.sysif, length, prot, flags, fd=self.near, offset=offset, page_size=page_size) return MemoryMapping(self.task, ret, file)
Inherited members
class MemoryMappingTask (sysif: SyscallInterface, near_process: Process, fd_table: FDTable, address_space: AddressSpace, pidns: PidNamespace)
-
A wrapper around
SyscallInterface
which tracks the namespaces of the underlying processNote that this is a base class for the more fully featured
Task
.We store namespace objects to represent the namespaces that we believe that underlying processes is in. Since we have complete control over the process, we can make sure this belief is accurate, by updating our stored namespaces when the process changes namespace. That isn't done here; it's done in handle.Task.
Currently, we store only one
PidNamespace
. But each process actually has two pid namespaces:- the process's own pid namespace, which determines the pids returned from getpid, clone, and other syscalls.
- the pid namespace that new children will be in.
The two pid namespaces only differ if we call unshare(CLONE.NEWPID). Currently we don't do that because unshare(CLONE.NEWPID) makes monitoring children more complex, since they can be deleted without leaving a zombie at any time if the pid namespace shuts down. But if we did call unshare(CLONE.NEWPID), we'd need to handle this right.
In the analogy to near and far pointers, this is like a segment register, if a segment register was write-only. Then we'd need to maintain the knowledge of what the segment register was set to, outside the segment register itself. That's what we do here.
There actually were systems where segment registers were, if not quite write-only, at least expensive to set and expensive to read. For example, x86_64 - the FS and GS segment registers can only be set via syscall. If you wanted to use segmentation on such systems, you'd probably have a structure much like this one.
Expand source code Browse git
class MemoryMappingTask(FileDescriptorTask): async def mmap(self, length: int, prot: PROT, flags: MAP, page_size: int=4096, ) -> MemoryMapping: # a mapping without a file descriptor, is an anonymous mapping flags |= MAP.ANONYMOUS ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size) return MemoryMapping(self, ret, rsyscall.far.File())
Ancestors
- FileDescriptorTask
- Task
- typing.Generic
Subclasses
Class variables
var sysif : SyscallInterface
var near_process : Process
var fd_table : FDTable
var address_space : AddressSpace
var pidns : PidNamespace
Methods
async def mmap(self, length: int, prot: PROT, flags: MAP, page_size: int = 4096) ‑> MemoryMapping
-
Expand source code Browse git
async def mmap(self, length: int, prot: PROT, flags: MAP, page_size: int=4096, ) -> MemoryMapping: # a mapping without a file descriptor, is an anonymous mapping flags |= MAP.ANONYMOUS ret = await _mmap(self.sysif, length, prot, flags, page_size=page_size) return MemoryMapping(self, ret, rsyscall.far.File())
Inherited members
class MemoryMapping (task: MemoryMappingTask, near: MemoryMapping, file: File)
-
MemoryMapping(task: 'MemoryMappingTask', near: 'rsyscall.near.MemoryMapping', file: 'rsyscall.far.File')
Expand source code Browse git
@dataclass(frozen=True) class MemoryMapping: __slots__ = ('task', 'near', 'file') task: MemoryMappingTask near: rsyscall.near.MemoryMapping file: rsyscall.far.File async def munmap(self) -> None: await _munmap(self.task.sysif, self.near) def for_task(self, task: MemoryMappingTask) -> MemoryMapping: if task.address_space != self.task.address_space: raise rsyscall.far.AddressSpaceMismatchError() return MemoryMapping(task, self.near, self.file) def __str__(self) -> str: return f"MemoryMapping({str(self.task)}, {str(self.near)})" def __repr__(self) -> str: return str(self)
Instance variables
var file : File
-
Return an attribute of instance, which is of type owner.
var near : MemoryMapping
-
Return an attribute of instance, which is of type owner.
var task : MemoryMappingTask
-
Return an attribute of instance, which is of type owner.
Methods
async def munmap(self) ‑> NoneType
-
Expand source code Browse git
async def munmap(self) -> None: await _munmap(self.task.sysif, self.near)
def for_task(self, task: MemoryMappingTask) ‑> MemoryMapping
-
Expand source code Browse git
def for_task(self, task: MemoryMappingTask) -> MemoryMapping: if task.address_space != self.task.address_space: raise rsyscall.far.AddressSpaceMismatchError() return MemoryMapping(task, self.near, self.file)