Skip to content
Snippets Groups Projects
start.rs 4.51 KiB
Newer Older
use alloc::vec::Vec;
use core::{intrinsics, ptr};
Jeremy Soller's avatar
Jeremy Soller committed

jD91mZM2's avatar
jD91mZM2 committed
use crate::{
    header::{stdio, stdlib},
    ld_so,
    platform::{self, new_mspace, types::*, Pal, Sys},
    ALLOCATOR,
jD91mZM2's avatar
jD91mZM2 committed
};
Jeremy Soller's avatar
Jeremy Soller committed

#[repr(C)]
pub struct Stack {
    pub argc: isize,
    pub argv0: *const c_char,
Jeremy Soller's avatar
Jeremy Soller committed
}

impl Stack {
    pub fn argv(&self) -> *const *const c_char {
        &self.argv0 as *const _
Jeremy Soller's avatar
Jeremy Soller committed
    }

    pub fn envp(&self) -> *const *const c_char {
        unsafe { self.argv().offset(self.argc + 1) }
Jeremy Soller's avatar
Jeremy Soller committed
    }

    pub fn auxv(&self) -> *const (usize, usize) {
        unsafe {
            let mut envp = self.envp();
            while !(*envp).is_null() {
                envp = envp.add(1);
            }
            envp.add(1) as *const (usize, usize)
        }
Jeremy Soller's avatar
Jeremy Soller committed
unsafe fn copy_string_array(array: *const *const c_char, len: usize) -> Vec<*mut c_char> {
    let mut vec = Vec::with_capacity(len + 1);
    for i in 0..len {
Mateusz Mikuła's avatar
Mateusz Mikuła committed
        let item = *array.add(i);
Jeremy Soller's avatar
Jeremy Soller committed
        let mut len = 0;
        while *item.add(len) != 0 {
            len += 1;
        }

        let buf = platform::alloc(len + 1) as *mut c_char;
        for i in 0..=len {
            *buf.add(i) = *item.add(i);
        }
        vec.push(buf);
    }
    vec.push(ptr::null_mut());
    vec
}

// Since Redox and Linux are so similar, it is easy to accidentally run a binary from one on the
// other. This will test that the current system is compatible with the current binary
#[no_mangle]
pub unsafe fn relibc_verify_host() {
    if !Sys::verify() {
        intrinsics::abort();
    }
}
#[link_section = ".init_array"]
#[used]
static INIT_ARRAY: [extern "C" fn(); 1] = [init_array];

static mut init_complete: bool = false;

fn alloc_init() {
    unsafe{
        if let Some(tcb) = ld_so::tcb::Tcb::current() {
            if tcb.mspace != 0 {
                ALLOCATOR.set_book_keeper(tcb.mspace);
            } else if ALLOCATOR.get_book_keeper() == 0 {
                ALLOCATOR.set_book_keeper(new_mspace());
            }
        } else if ALLOCATOR.get_book_keeper() == 0 {
            ALLOCATOR.set_book_keeper(new_mspace());
        }
    }
}

extern "C" fn init_array() {
    // The thing is that we cannot guarantee if
    // init_array runs first or if relibc_start runs first
    // Still whoever gets to run first must initialize rust
    // memory allocator before doing anything else.
    alloc_init();
    io_init();
    unsafe { init_complete = true };
}
fn io_init() {
    unsafe {
        // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718
        stdio::stdin = stdio::default_stdin.get();
        stdio::stdout = stdio::default_stdout.get();
        stdio::stderr = stdio::default_stderr.get();
    }
}
Jeremy Soller's avatar
Jeremy Soller committed
#[inline(never)]
#[no_mangle]
pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! {
Jeremy Soller's avatar
Jeremy Soller committed
    extern "C" {
        static __preinit_array_start: extern "C" fn();
Jeremy Soller's avatar
Jeremy Soller committed
        static __preinit_array_end: extern "C" fn();
        static __init_array_start: extern "C" fn();
        static __init_array_end: extern "C" fn();
Jeremy Soller's avatar
Jeremy Soller committed
        fn _init();
Jeremy Soller's avatar
Jeremy Soller committed
        fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int;
Jeremy Soller's avatar
Jeremy Soller committed
    }
    // Step 1 setup the right allocator...
    // if any memory rust based memory allocation happen before this step .. we are doomed.
    alloc_init();
Jeremy Soller's avatar
Jeremy Soller committed

    // Ensure correct host system before executing more system calls
    relibc_verify_host();

    ld_so::init(sp);

Jeremy Soller's avatar
Jeremy Soller committed
    // Set up argc and argv
Jeremy Soller's avatar
Jeremy Soller committed
    let argv = sp.argv();
Jeremy Soller's avatar
Jeremy Soller committed
    platform::inner_argv = copy_string_array(argv, argc as usize);
    platform::argv = platform::inner_argv.as_mut_ptr();

Jeremy Soller's avatar
Jeremy Soller committed
    // Set up envp
Jeremy Soller's avatar
Jeremy Soller committed
    let envp = sp.envp();
    let mut len = 0;
Jeremy Soller's avatar
Jeremy Soller committed
    while !(*envp.add(len)).is_null() {
Jeremy Soller's avatar
Jeremy Soller committed
        len += 1;
    }
Jeremy Soller's avatar
Jeremy Soller committed
    platform::inner_environ = copy_string_array(envp, len);
Jeremy Soller's avatar
Jeremy Soller committed
    platform::environ = platform::inner_environ.as_mut_ptr();

    if !init_complete {
        init_array();
    }
    // Run preinit array
    {
        let mut f = &__preinit_array_start as *const _;
Mateusz Mikuła's avatar
Mateusz Mikuła committed
        #[allow(clippy::op_ref)]
        while f < &__preinit_array_end {
            (*f)();
            f = f.offset(1);
        }
    }

Jeremy Soller's avatar
Jeremy Soller committed
    // Call init section
Jeremy Soller's avatar
Jeremy Soller committed
    _init();

    // Run init array
    {
        let mut f = &__init_array_start as *const _;
Mateusz Mikuła's avatar
Mateusz Mikuła committed
        #[allow(clippy::op_ref)]
        while f < &__init_array_end {
            (*f)();
            f = f.offset(1);
        }
Jeremy Soller's avatar
Jeremy Soller committed
    // not argv or envp, because programs like bash try to modify this *const* pointer :|
Jeremy Soller's avatar
Jeremy Soller committed
    stdlib::exit(main(argc, platform::argv, platform::environ));
Jeremy Soller's avatar
Jeremy Soller committed

    unreachable!();
Jeremy Soller's avatar
Jeremy Soller committed
}