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