1 use alloc::vec::Vec; 2 use core::{intrinsics, ptr}; 3 4 use crate::{ 5 header::{libgen, stdio, stdlib}, 6 ld_so, 7 platform::{self, new_mspace, types::*, Pal, Sys}, 8 ALLOCATOR, 9 }; 10 11 #[repr(C)] 12 pub struct Stack { 13 pub argc: isize, 14 pub argv0: *const c_char, 15 } 16 17 impl Stack { 18 pub fn argv(&self) -> *const *const c_char { 19 &self.argv0 as *const _ 20 } 21 22 pub fn envp(&self) -> *const *const c_char { 23 unsafe { self.argv().offset(self.argc + 1) } 24 } 25 26 pub fn auxv(&self) -> *const (usize, usize) { 27 unsafe { 28 let mut envp = self.envp(); 29 while !(*envp).is_null() { 30 envp = envp.add(1); 31 } 32 envp.add(1) as *const (usize, usize) 33 } 34 } 35 } 36 37 unsafe fn copy_string_array(array: *const *const c_char, len: usize) -> Vec<*mut c_char> { 38 let mut vec = Vec::with_capacity(len + 1); 39 for i in 0..len { 40 let item = *array.add(i); 41 let mut len = 0; 42 while *item.add(len) != 0 { 43 len += 1; 44 } 45 46 let buf = platform::alloc(len + 1) as *mut c_char; 47 for i in 0..=len { 48 *buf.add(i) = *item.add(i); 49 } 50 vec.push(buf); 51 } 52 vec.push(ptr::null_mut()); 53 vec 54 } 55 56 // Since Redox and Linux are so similar, it is easy to accidentally run a binary from one on the 57 // other. This will test that the current system is compatible with the current binary 58 #[no_mangle] 59 pub unsafe fn relibc_verify_host() { 60 if !Sys::verify() { 61 intrinsics::abort(); 62 } 63 } 64 #[link_section = ".init_array"] 65 #[used] 66 static INIT_ARRAY: [extern "C" fn(); 1] = [init_array]; 67 68 static mut init_complete: bool = false; 69 70 #[used] 71 #[no_mangle] 72 static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut(); 73 74 fn alloc_init() { 75 unsafe { 76 if init_complete { 77 return; 78 } 79 } 80 unsafe { 81 if let Some(tcb) = ld_so::tcb::Tcb::current() { 82 if tcb.mspace != 0 { 83 ALLOCATOR.set_book_keeper(tcb.mspace); 84 } else if ALLOCATOR.get_book_keeper() == 0 { 85 ALLOCATOR.set_book_keeper(new_mspace()); 86 } 87 } else if ALLOCATOR.get_book_keeper() == 0 { 88 ALLOCATOR.set_book_keeper(new_mspace()); 89 } 90 } 91 } 92 93 extern "C" fn init_array() { 94 // The thing is that we cannot guarantee if 95 // init_array runs first or if relibc_start runs first 96 // Still whoever gets to run first must initialize rust 97 // memory allocator before doing anything else. 98 99 unsafe { 100 if init_complete { 101 return; 102 } 103 } 104 105 alloc_init(); 106 io_init(); 107 108 unsafe { 109 if platform::environ.is_null() { 110 platform::environ = __relibc_init_environ; 111 } 112 } 113 114 extern "C" { 115 fn pthread_init(); 116 } 117 unsafe { 118 pthread_init(); 119 init_complete = true 120 } 121 } 122 fn io_init() { 123 unsafe { 124 // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718 125 stdio::stdin = stdio::default_stdin.get(); 126 stdio::stdout = stdio::default_stdout.get(); 127 stdio::stderr = stdio::default_stderr.get(); 128 } 129 } 130 131 #[cfg(target_os = "redox")] 132 fn setup_sigstack() { 133 use syscall::{Map, MapFlags}; 134 const SIGSTACK_SIZE: usize = 1024 * 256; 135 let sigstack = unsafe { syscall::fmap(!0, &Map { address: 0, offset: 0, flags: MapFlags::MAP_PRIVATE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, size: SIGSTACK_SIZE }) }.expect("failed to allocate sigstack") + SIGSTACK_SIZE; 136 137 let fd = syscall::open("thisproc:current/sigstack", syscall::O_WRONLY | syscall::O_CLOEXEC).expect("failed to open thisproc:current/sigstack"); 138 syscall::write(fd, &usize::to_ne_bytes(sigstack)).expect("failed to write to thisproc:current/sigstack"); 139 let _ = syscall::close(fd); 140 } 141 142 #[inline(never)] 143 #[no_mangle] 144 pub unsafe extern "C" fn relibc_start(sp: &'static Stack) -> ! { 145 extern "C" { 146 static __preinit_array_start: extern "C" fn(); 147 static __preinit_array_end: extern "C" fn(); 148 static __init_array_start: extern "C" fn(); 149 static __init_array_end: extern "C" fn(); 150 151 fn _init(); 152 fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int; 153 } 154 155 // Ensure correct host system before executing more system calls 156 relibc_verify_host(); 157 158 // Initialize TLS, if necessary 159 ld_so::init(sp); 160 161 // Set up the right allocator... 162 // if any memory rust based memory allocation happen before this step .. we are doomed. 163 alloc_init(); 164 165 // Set up argc and argv 166 let argc = sp.argc; 167 let argv = sp.argv(); 168 platform::inner_argv = copy_string_array(argv, argc as usize); 169 platform::argv = platform::inner_argv.as_mut_ptr(); 170 // Special code for program_invocation_name and program_invocation_short_name 171 if let Some(arg) = platform::inner_argv.get(0) { 172 platform::program_invocation_name = *arg; 173 platform::program_invocation_short_name = libgen::basename(*arg); 174 } 175 // We check for NULL here since ld.so might already have initialized it for us, and we don't 176 // want to overwrite it if constructors in .init_array of dependency libraries have called 177 // setenv. 178 if platform::environ.is_null() { 179 // Set up envp 180 let envp = sp.envp(); 181 let mut len = 0; 182 while !(*envp.add(len)).is_null() { 183 len += 1; 184 } 185 platform::OUR_ENVIRON = copy_string_array(envp, len); 186 platform::environ = platform::OUR_ENVIRON.as_mut_ptr(); 187 } 188 189 // Setup signal stack, otherwise we cannot handle any signals besides SIG_IGN/SIG_DFL behavior. 190 #[cfg(target_os = "redox")] 191 setup_sigstack(); 192 193 init_array(); 194 195 // Run preinit array 196 { 197 let mut f = &__preinit_array_start as *const _; 198 #[allow(clippy::op_ref)] 199 while f < &__preinit_array_end { 200 (*f)(); 201 f = f.offset(1); 202 } 203 } 204 205 // Call init section 206 _init(); 207 208 // Run init array 209 { 210 let mut f = &__init_array_start as *const _; 211 #[allow(clippy::op_ref)] 212 while f < &__init_array_end { 213 (*f)(); 214 f = f.offset(1); 215 } 216 } 217 218 // not argv or envp, because programs like bash try to modify this *const* pointer :| 219 stdlib::exit(main(argc, platform::argv, platform::environ)); 220 221 unreachable!(); 222 } 223