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