Files
RedBear-OS/src/start.rs
T

260 lines
7.3 KiB
Rust

//! Startup code.
use alloc::{boxed::Box, vec::Vec};
use core::ptr;
#[cfg(target_os = "redox")]
use generic_rt::ExpectTlsFree;
use crate::{
ALLOCATOR,
header::{libgen, stdio, stdlib},
ld_so::{self, linker::Linker},
platform::{self, Pal, Sys, get_auxvs, types::*},
sync::mutex::Mutex,
};
#[repr(C)]
pub struct Stack {
pub argc: isize,
pub argv0: *const c_char,
}
impl Stack {
pub fn argv(&self) -> *const *const c_char {
ptr::from_ref(&self.argv0)
}
pub fn envp(&self) -> *const *const c_char {
unsafe { self.argv().offset(self.argc + 1) }
}
pub fn auxv(&self) -> *const (usize, usize) {
unsafe {
let mut envp = self.envp();
while !(*envp).is_null() {
envp = envp.add(1);
}
envp.add(1).cast::<(usize, usize)>()
}
}
}
unsafe fn copy_string_array(array: *const *const c_char, len: usize) -> Vec<*mut c_char> {
use crate::header::string::strlen;
let mut vec = Vec::with_capacity(len + 1);
let mut lengths = Vec::with_capacity(len);
let mut size = 0;
for i in 0..len {
let item = unsafe { *array.add(i) };
lengths.push(unsafe { strlen(item) } + 1);
size += lengths[i];
}
// Programs unfortunately rely on the strings being contiguous in memory. For example:
// https://github.com/libuv/libuv/blob/12d0dd48e3c6baf1e2f0d9f85f11f0ef58285d6f/src/unix/proctitle.c#L87
let mut offset = 0;
let buf = unsafe { platform::alloc(size).cast::<c_char>() };
for i in 0..len {
let dest_buf = unsafe { buf.add(offset) };
let item = unsafe { *array.add(i) };
let len = lengths[i];
unsafe {
ptr::copy_nonoverlapping(item, dest_buf, len);
}
vec.push(dest_buf);
offset += len;
}
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
#[unsafe(no_mangle)]
pub unsafe fn relibc_verify_host() {
if !Sys::verify() {
unsafe { stdlib::abort() };
}
}
#[unsafe(link_section = ".init_array")]
#[used]
static INIT_ARRAY: [extern "C" fn(); 1] = [init_array];
static mut INIT_COMPLETE: bool = false;
#[used]
#[unsafe(no_mangle)]
static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut();
fn alloc_init() {
unsafe {
if INIT_COMPLETE {
return;
}
}
unsafe {
if let Some(tcb) = ld_so::tcb::Tcb::current()
&& !tcb.mspace.is_null()
{
ALLOCATOR.set(tcb.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.
unsafe {
if INIT_COMPLETE {
return;
}
}
alloc_init();
io_init();
unsafe {
if platform::environ.is_null() {
platform::environ = __relibc_init_environ;
}
}
unsafe {
crate::pthread::init();
INIT_COMPLETE = true
}
}
fn io_init() {
unsafe {
// Initialize stdin/stdout/stderr.
// TODO: const fn initialization of FILE
stdio::stdin = stdio::default_stdin().get();
stdio::stdout = stdio::default_stdout().get();
stdio::stderr = stdio::default_stderr().get();
}
}
#[inline(never)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn relibc_start_v1(
sp: &'static Stack,
main: unsafe extern "C" fn(
argc: isize,
argv: *mut *mut c_char,
envp: *mut *mut c_char,
) -> c_int,
) -> ! {
unsafe extern "C" {
static __preinit_array_start: extern "C" fn();
static __preinit_array_end: extern "C" fn();
static __init_array_start: extern "C" fn();
static __init_array_end: extern "C" fn();
}
// Ensure correct host system before executing more system calls
unsafe { relibc_verify_host() };
#[cfg(target_os = "redox")]
let thr_fd = redox_rt::proc::FdGuard::new(
unsafe {
crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD)
}
.expect_notls("no thread fd present"),
)
.to_upper()
.expect_notls("failed to move thread fd to upper table");
// Initialize TLS, if necessary
unsafe {
ld_so::init(
sp,
#[cfg(target_os = "redox")]
thr_fd,
)
};
// Set up the right allocator...
// if any memory rust based memory allocation happen before this step .. we are doomed.
alloc_init();
if let Some(tcb) = unsafe { ld_so::tcb::Tcb::current() } {
// Update TCB mspace
if tcb.mspace.is_null() {
tcb.mspace = ALLOCATOR.get();
}
// Set linker pointer if necessary
if tcb.linker_ptr.is_null() {
//TODO: get ld path
let linker = Linker::new(ld_so::linker::Config::default());
//TODO: load root object
tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
}
#[cfg(target_os = "redox")]
redox_rt::signal::setup_sighandler(&tcb.os_specific, true);
}
// Set up argc and argv
let argc = sp.argc;
let argv = sp.argv();
unsafe { platform::inner_argv.unsafe_set(copy_string_array(argv, argc as usize)) };
unsafe { platform::argv = platform::inner_argv.unsafe_mut().as_mut_ptr() };
// Special code for program_invocation_name and program_invocation_short_name
if let Some(arg) = unsafe { platform::inner_argv.unsafe_ref() }.first() {
unsafe { platform::program_invocation_name = *arg };
unsafe { platform::program_invocation_short_name = libgen::basename(*arg) };
}
// We check for NULL here since ld.so might already have initialized it for us, and we don't
// want to overwrite it if constructors in .init_array of dependency libraries have called
// setenv.
if unsafe { platform::environ }.is_null() {
// Set up envp
let envp = sp.envp();
let mut len = 0;
while !(unsafe { *envp.add(len) }).is_null() {
len += 1;
}
unsafe { platform::OUR_ENVIRON.unsafe_set(copy_string_array(envp, len)) };
unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
}
let auxvs = unsafe { get_auxvs(sp.auxv().cast()) };
unsafe { crate::platform::init(auxvs) };
init_array();
unsafe { crate::platform::logger::init() };
// Run preinit array
{
let mut f = unsafe { &__preinit_array_start } as *const _;
#[allow(clippy::op_ref)]
while f < &raw const __preinit_array_end {
(unsafe { *f })();
f = unsafe { f.offset(1) };
}
}
// Run init array
{
let mut f = unsafe { &__init_array_start } as *const _;
#[allow(clippy::op_ref)]
while f < &raw const __init_array_end {
(unsafe { *f })();
f = unsafe { f.offset(1) };
}
}
// not argv or envp, because programs like bash try to modify this *const* pointer :|
unsafe { stdlib::exit(main(argc, platform::argv, platform::environ)) };
unreachable!();
}