lx 0.4.0

A no_std crate to use Linux system calls
Documentation
//! On startup, Linux calls the entry point function with the application arguments, environment
//! variables and ELF auxiliary header on the stack.
//!
//! This does not follow a calling convention that Rust understands so it is not possible to write
//! a pure Rust function that accesses these things.
//!
//! This modules provides a `start` macro to define an entry point that calls into a user provided
//! Rust function with an argument slice and a `EnvVars` object so that it can read the information
//! from Linux.
//!
//! Once the application has finished reading the arguments with the `Args` object, it can call
//! `into_env_vars` to get a `EnvVars` object to read the environment variables. Similarly, once
//! the application has finished reading the environment variables with the `EnvVars` object, it
//! can call `into_elf_aux_table` to get a `ElfAuxTable` object to read the ELF auxiliary table.
//!
//! The reading is done this way because except for the program arguments, arrays are not length
//! prefixed but rather null-terminated so there is no way to seek to the interesting data and the
//! data must always be read in order.
//!
//! If the program does not care about these things, it can create its own `_start` function.
//!
//! In both case, please note that the program's entry point is not supposed to return. Instead,
//! the program should call `exit`.

use core::ffi::c_char;

// ELF auxiliary entry keys.
pub const AT_NULL: usize = 0;
pub const AT_IGNORE: usize = 1;
pub const AT_EXECFD: usize = 2;
pub const AT_PHDR: usize = 3;
pub const AT_PHENT: usize = 4;
pub const AT_PHNUM: usize = 5;
pub const AT_PAGESZ: usize = 6;
pub const AT_BASE: usize = 7;
pub const AT_FLAGS: usize = 8;
pub const AT_ENTRY: usize = 9;
pub const AT_NOTELF: usize = 10;
pub const AT_UID: usize = 11;
pub const AT_EUID: usize = 12;
pub const AT_GID: usize = 13;
pub const AT_EGID: usize = 14;
pub const AT_PLATFORM: usize = 15;
pub const AT_HWCAP: usize = 16;
pub const AT_CLKTCK: usize = 17;
pub const AT_SECURE: usize = 23;
pub const AT_BASE_PLATFORM: usize = 24;
pub const AT_RANDOM: usize = 25;
pub const AT_HWCAP2: usize = 26;
pub const AT_EXECFN: usize = 31;
pub const AT_MINSIGSTKSZ: usize = 51;

/// Environment variable iterator.
///
/// # Invariants
///
/// `curr` must be properly aligned.
///
/// There must exist a `n` such that `curr.add(n)` is valid for reads and `*curr.offset(n)` is
/// `ptr::null()`, indicating the end of the array.
///
/// Pointers of the form `curr.add(i)` with `i` from zero included to `n` excluded must be valid
/// for reads.
///
/// There must be a valid ELF auxiliary table at `curr.add(n + 1)`. That is,
/// `ElfAuxTable { curr: curr.add(n + 1) as *const ElfAuxEntry }` must follow `ElfAuxTable`'s
/// invariants.
pub struct EnvVars {
    curr: *const *const c_char,
}

impl EnvVars {
    /// Creates a new environment variable iterator from a pointer to a Linux-like environment
    /// variable array.
    ///
    /// # Safety
    ///
    /// `start` must be properly aligned.
    ///
    /// There must exist a `n` such that `start.add(n)` is valid for reads and `*start.offset(n)`
    /// is `ptr::null()`, indicating the end of the array.
    ///
    /// Pointers of the form `start.add(i)` with `i` from zero included to `n` excluded must be
    /// valid for reads.
    ///
    /// There must be a valid ELF auxiliary table at `start.add(n + 1)`. That is,
    /// `ElfAuxTable { curr: start.add(n + 1) as *const ElfAuxEntry }` must follow `ElfAuxTable`'s
    /// invariants.
    pub unsafe fn new(start: *const *const c_char) -> Self {
        Self { curr: start }
    }

    pub fn into_elf_aux_table(mut self) -> ElfAuxTable {
        // Skip remaining variables.
        while self.next().is_some() {}
        let start = unsafe { self.curr.offset(1) as *const ElfAuxEntry };
        ElfAuxTable { curr: start }
    }
}

impl Iterator for EnvVars {
    type Item = *const c_char;

    fn next(&mut self) -> Option<Self::Item> {
        let entry = unsafe { *self.curr };
        if entry.is_null() {
            return None;
        }
        self.curr = unsafe { self.curr.offset(1) };
        Some(entry)
    }
}

/// An ELF auxiliary table entry.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct ElfAuxEntry {
    pub key: usize,
    pub val: usize,
}

/// An ELF auxiliary table. See the `AT_*` constants for entries that can be found inside.
///
/// # Invariants
///
/// `curr` must be aligned properly.
///
/// There must exist a `n` such that `curr.add(n)` is valid for reads and `(*curr.add(n)).key` is
/// `AT_NULL`, indicating the end of the table.
///
/// Pointers of the form `curr.offset(i)` with `i` from zero included to `n` excluded must be valid
/// for reads.
pub struct ElfAuxTable {
    curr: *const ElfAuxEntry,
}

impl Iterator for ElfAuxTable {
    type Item = ElfAuxEntry;

    fn next(&mut self) -> Option<Self::Item> {
        let entry = unsafe { *self.curr };
        if entry.key == AT_NULL {
            return None;
        }
        self.curr = unsafe { self.curr.offset(1) };
        Some(entry)
    }
}

#[macro_export]
macro_rules! def_start {
    ($user_entry:ident) => {
        ::core::arch::global_asm!(
            "
        .section .text
        .global _start
        _start:
            mov rdi, rsp
            jmp rust_start
        "
        );

        #[no_mangle]
        unsafe extern "C" fn rust_start(mut stack: *const usize) -> ! {
            use ::core::ffi::c_char;

            let argc = *stack;
            stack = stack.offset(1);

            let args = ::core::slice::from_raw_parts(stack as *const *const c_char, argc);
            stack = stack.add(argc);
            stack = stack.offset(1);

            let env_vars = $crate::EnvVars::new(stack as *const *const c_char);

            $user_entry(args, env_vars)
        }
    };
}