xref: /relibc/src/start.rs (revision 7d27737c3ffa5bbdf5d00592bb95abf1f9703aee)
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