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