lx 0.4.0

A no_std crate to use Linux system calls
Documentation
use core::{
    mem::ManuallyDrop,
    ptr,
};

use super::abi::*;
use crate::{
    eprintln,
    result_from_value,
    unit_result_from_value,
    AsRawFd,
    RawFd,
};

pub const MAP_SHARED: u32 = 0x1;
pub const MAP_PRIVATE: u32 = 0x2;
pub const MAP_SHARED_VALIDATE: u32 = 0x3;
pub const MAP_FIXED: u32 = 0x10;
pub const MAP_ANONYMOUS: u32 = 0x20;
pub const MAP_GROWSDOWN: u32 = 0x100;
pub const MAP_LOCKED: u32 = 0x2000;
pub const MAP_NORESERVE: u32 = 0x4000;
pub const MAP_POPULATE: u32 = 0x8000;
pub const MAP_NONBLOCK: u32 = 0x10000;
pub const MAP_STACK: u32 = 0x20000;
pub const MAP_HUGETLB: u32 = 0x40000;
pub const MAP_SYNC: u32 = 0x80000;
pub const MAP_FIXED_NOREPLACE: u32 = 0x100000;

pub const MREMAP_MAYMOVE: u32 = 0x1;
pub const MREMAP_FIXED: u32 = 0x2;
pub const MREMAP_DONTUNMAP: u32 = 0x4;

pub const PROT_NONE: u32 = 0;
pub const PROT_READ: u32 = 1;
pub const PROT_WRITE: u32 = 2;
pub const PROT_EXEC: u32 = 4;

/// A memory mapping that was mapped application using `mmap`. `munmap` is automatically called
/// when the object is dropped.
///
/// # Invariants
///
/// The address in `mem` must be a multiple of the `PAGE_SIZE` and of the mapping's huge page
/// size if the mapping employs huge pages.
///
/// `mem`'s length must not be zero.
pub struct Mmap {
    // `NonNull` and `Unique` cannot be used because they require the pointer to be non-null. A
    // mutable pointer.
    mem: *mut [u8],
}

unsafe impl Send for Mmap {}
unsafe impl Sync for Mmap {}

impl Mmap {
    /// Creates a new `Mmap` object from a slice that was mapped with `mmap`.
    ///
    /// # Safety
    ///
    /// The address in `mem` must be a multiple of the `PAGE_SIZE` and of the mapping's huge page
    /// size if the mapping employs huge pages.
    ///
    /// `mem`'s length must not be zero.
    pub unsafe fn from_raw(mem: *mut [u8]) -> Self {
        Self { mem }
    }

    /// Splits the memory mapping into two sub-mappings:
    /// 1. from zero included to `offset` excluded, and
    /// 2. from `offset` included to the end.
    ///
    /// # Safety
    ///
    /// `offset` must be a multiple of the `PAGE_SIZE` and of the mapping's huge page size if the
    /// mapping employs huge pages.
    ///
    /// `offset` must not be zero or be larger than or equal to the mapping's size as empty
    /// (sub-)mappings are invalid according to `Mmap`'s invariants.
    pub unsafe fn split_at(self, offset: usize) -> (Self, Self) {
        let mut this = ManuallyDrop::new(self);
        let (first, second) = this.as_mut().split_at_mut(offset);
        (Mmap::from_raw(first), Mmap::from_raw(second))
    }

    /// Consumes the `Mmap`, returning a wrapped raw pointer.
    ///
    /// After calling this function, the caller is responsible for the memory mapping.
    pub fn into_raw(self) -> *mut [u8] {
        let this = ManuallyDrop::new(self);
        this.mem
    }
}

impl AsRef<[u8]> for Mmap {
    fn as_ref(&self) -> &[u8] {
        unsafe { &*self.mem }
    }
}

impl AsMut<[u8]> for Mmap {
    fn as_mut(&mut self) -> &mut [u8] {
        unsafe { &mut *self.mem }
    }
}

impl Drop for Mmap {
    fn drop(&mut self) {
        if let Err(e) = unsafe { munmap(self.mem) } {
            eprintln!(
                "munmap({addr:p}, {len}): {e}",
                addr = self.as_ref().as_ptr(),
                len = self.as_ref().len(),
            );
        }
    }
}

/// Maps a chunk of memory.
///
/// # Safety
///
/// If the map is a shared file map, then the application must be careful about potential undefined
/// behavior if the underlying file can change, in or out of process.
#[inline]
pub unsafe fn mmap(
    addr: *mut u8,
    len: usize,
    prot: u32,
    flags: u32,
    fd: RawFd,
    off: usize,
) -> crate::Result<Mmap> {
    let ret = syscall_6(
        9,
        addr as usize,
        len,
        prot as usize,
        flags as usize,
        fd as isize as usize,
        off,
    );
    let final_addr = result_from_value(ret)?;
    let mem = ptr::slice_from_raw_parts_mut(final_addr as *mut u8, len);
    Ok(Mmap { mem })
}

/// Creates a file-backed memory mapping.
///
/// # Safety
///
/// If the map is shared, then the application must be careful about potential undefined behavior
/// if the underlying file changes, in or out of process.
#[inline]
pub unsafe fn mmap_file(
    addr: *mut u8,
    len: usize,
    prot: u32,
    flags: u32,
    fd: &impl AsRawFd,
    off: usize,
) -> crate::Result<Mmap> {
    mmap(addr, len, prot, flags, fd.as_raw_fd(), off)
}

/// Creates an anonymous memory mapping.
#[inline]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn mmap_anonymous(addr: *mut u8, len: usize, prot: u32, flags: u32) -> crate::Result<Mmap> {
    unsafe { mmap(addr, len, prot, flags | MAP_ANONYMOUS, -1, 0) }
}

/// Grows, shrinks or moves a memory mapping.
///
/// # Safety
///
/// The application must ensure that the memory that is unmapped or moved is no longer used.
#[inline]
pub unsafe fn mremap(
    mmap: &mut Mmap,
    new_len: usize,
    flags: u32,
    new_addr: *mut u8,
) -> crate::Result<()> {
    let ret = syscall_5(
        25,
        mmap.as_mut().as_mut_ptr() as usize,
        mmap.as_mut().len(),
        new_len,
        flags as usize,
        new_addr as usize,
    );
    let final_addr = result_from_value(ret)?;
    let mem = ptr::slice_from_raw_parts_mut(final_addr as *mut u8, new_len);
    mmap.mem = mem;
    Ok(())
}

/// Modifies a memory mapping's protection flags.
#[inline]
pub fn mprotect(mmap: &Mmap, prot: u32) -> crate::Result<()> {
    let ret = unsafe {
        syscall_3(
            10,
            mmap.as_ref().as_ptr() as usize,
            mmap.as_ref().len(),
            prot as usize,
        ) as i32
    };
    unit_result_from_value(ret)
}

/// Unmaps a memory mapping.
///
/// # Safety
///
/// The application must ensure that the memory that is unmapped is no longer used.
#[inline]
pub unsafe fn munmap(mem: *mut [u8]) -> crate::Result<()> {
    let mem = &mut *mem;
    let ret = syscall_2(11, mem.as_mut_ptr() as usize, mem.len()) as i32;
    unit_result_from_value(ret)
}