Skip to main content

relibc/
start.rs

1//! Startup code.
2
3use alloc::{boxed::Box, vec::Vec};
4use core::{intrinsics, ptr};
5
6#[cfg(target_os = "redox")]
7use generic_rt::ExpectTlsFree;
8
9use crate::{
10    ALLOCATOR,
11    header::{libgen, stdio, stdlib},
12    ld_so::{self, linker::Linker},
13    platform::{self, Pal, Sys, get_auxvs, types::*},
14    sync::mutex::Mutex,
15};
16
17#[repr(C)]
18pub struct Stack {
19    pub argc: isize,
20    pub argv0: *const c_char,
21}
22
23impl Stack {
24    pub fn argv(&self) -> *const *const c_char {
25        ptr::from_ref(&self.argv0)
26    }
27
28    pub fn envp(&self) -> *const *const c_char {
29        unsafe { self.argv().offset(self.argc + 1) }
30    }
31
32    pub fn auxv(&self) -> *const (usize, usize) {
33        unsafe {
34            let mut envp = self.envp();
35            while !(*envp).is_null() {
36                envp = envp.add(1);
37            }
38            envp.add(1).cast::<(usize, usize)>()
39        }
40    }
41}
42
43unsafe fn copy_string_array(array: *const *const c_char, len: usize) -> Vec<*mut c_char> {
44    use crate::header::string::strlen;
45
46    let mut vec = Vec::with_capacity(len + 1);
47    let mut lengths = Vec::with_capacity(len);
48    let mut size = 0;
49    for i in 0..len {
50        let item = unsafe { *array.add(i) };
51        lengths.push(unsafe { strlen(item) } + 1);
52        size += lengths[i];
53    }
54
55    // Programs unfortunately rely on the strings being contiguous in memory. For example:
56    // https://github.com/libuv/libuv/blob/12d0dd48e3c6baf1e2f0d9f85f11f0ef58285d6f/src/unix/proctitle.c#L87
57    let mut offset = 0;
58    let buf = unsafe { platform::alloc(size).cast::<c_char>() };
59
60    #[expect(clippy::needless_range_loop)]
61    for i in 0..len {
62        let dest_buf = unsafe { buf.add(offset) };
63        let item = unsafe { *array.add(i) };
64        let len = lengths[i];
65
66        unsafe {
67            ptr::copy_nonoverlapping(item, dest_buf, len);
68        }
69
70        vec.push(dest_buf);
71        offset += len;
72    }
73    vec.push(ptr::null_mut());
74    vec
75}
76
77// Since Redox and Linux are so similar, it is easy to accidentally run a binary from one on the
78// other. This will test that the current system is compatible with the current binary
79#[unsafe(no_mangle)]
80pub unsafe fn relibc_verify_host() {
81    if !Sys::verify() {
82        intrinsics::abort();
83    }
84}
85#[unsafe(link_section = ".init_array")]
86#[used]
87static INIT_ARRAY: [extern "C" fn(); 1] = [init_array];
88
89static mut INIT_COMPLETE: bool = false;
90
91#[used]
92#[unsafe(no_mangle)]
93static mut __relibc_init_environ: *mut *mut c_char = ptr::null_mut();
94
95fn alloc_init() {
96    unsafe {
97        if INIT_COMPLETE {
98            return;
99        }
100    }
101    unsafe {
102        if let Some(tcb) = ld_so::tcb::Tcb::current()
103            && !tcb.mspace.is_null()
104        {
105            ALLOCATOR.set(tcb.mspace);
106        }
107    }
108}
109
110extern "C" fn init_array() {
111    // The thing is that we cannot guarantee if
112    // init_array runs first or if relibc_start runs first
113    // Still whoever gets to run first must initialize rust
114    // memory allocator before doing anything else.
115
116    unsafe {
117        if INIT_COMPLETE {
118            return;
119        }
120    }
121
122    alloc_init();
123    io_init();
124
125    unsafe {
126        if platform::environ.is_null() {
127            platform::environ = __relibc_init_environ;
128        }
129    }
130
131    unsafe {
132        crate::pthread::init();
133        INIT_COMPLETE = true
134    }
135}
136
137fn io_init() {
138    unsafe {
139        // Initialize stdin/stdout/stderr.
140        // TODO: const fn initialization of FILE
141        stdio::stdin = stdio::default_stdin().get();
142        stdio::stdout = stdio::default_stdout().get();
143        stdio::stderr = stdio::default_stderr().get();
144    }
145}
146
147#[inline(never)]
148#[unsafe(no_mangle)]
149pub unsafe extern "C" fn relibc_start_v1(
150    sp: &'static Stack,
151    main: unsafe extern "C" fn(
152        argc: isize,
153        argv: *mut *mut c_char,
154        envp: *mut *mut c_char,
155    ) -> c_int,
156) -> ! {
157    unsafe extern "C" {
158        static __preinit_array_start: extern "C" fn();
159        static __preinit_array_end: extern "C" fn();
160        static __init_array_start: extern "C" fn();
161        static __init_array_end: extern "C" fn();
162    }
163
164    // Ensure correct host system before executing more system calls
165    unsafe { relibc_verify_host() };
166
167    #[cfg(target_os = "redox")]
168    let thr_fd = redox_rt::proc::FdGuard::new(
169        unsafe {
170            crate::platform::get_auxv_raw(sp.auxv().cast(), redox_rt::auxv_defs::AT_REDOX_THR_FD)
171        }
172        .expect_notls("no thread fd present"),
173    )
174    .to_upper()
175    .expect_notls("failed to move thread fd to upper table");
176
177    #[cfg(target_os = "redox")]
178    {
179        if redox_rt::current_filetable().fd().is_none() {
180            let filetable_fd = unsafe {
181                crate::platform::get_auxv_raw(
182                    sp.auxv().cast(),
183                    redox_rt::auxv_defs::AT_REDOX_FILETABLE_FD,
184                )
185            }
186            .expect_notls("no filetable fd present");
187            let filetable_guard = redox_rt::proc::FdGuard::new(filetable_fd)
188                .to_upper()
189                .expect_notls("failed to move filetable fd to upper table");
190            *redox_rt::current_filetable() = redox_rt::sys::FdTbl::from_binary_fd(filetable_guard)
191                .expect_notls("failed to initialize FILETABLE");
192        }
193    }
194
195    // Initialize TLS, if necessary
196    unsafe {
197        ld_so::init(
198            sp,
199            #[cfg(target_os = "redox")]
200            thr_fd,
201        )
202    };
203
204    // Set up the right allocator...
205    // if any memory rust based memory allocation happen before this step .. we are doomed.
206    alloc_init();
207
208    if let Some(tcb) = unsafe { ld_so::tcb::Tcb::current() } {
209        // Update TCB mspace
210        if tcb.mspace.is_null() {
211            tcb.mspace = ALLOCATOR.get();
212        }
213
214        // Set linker pointer if necessary
215        if tcb.linker_ptr.is_null() {
216            //TODO: get ld path
217            let linker = Linker::new(ld_so::linker::Config::default());
218            //TODO: load root object
219            tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
220        }
221        #[cfg(target_os = "redox")]
222        redox_rt::signal::setup_sighandler(&tcb.os_specific, true);
223    }
224
225    // Set up argc and argv
226    let argc = sp.argc;
227    let argv = sp.argv();
228    unsafe { platform::inner_argv.unsafe_set(copy_string_array(argv, argc as usize)) };
229    unsafe { platform::argv = platform::inner_argv.unsafe_mut().as_mut_ptr() };
230    // Special code for program_invocation_name and program_invocation_short_name
231    if let Some(arg) = unsafe { platform::inner_argv.unsafe_ref() }.first() {
232        unsafe { platform::program_invocation_name = *arg };
233        unsafe { platform::program_invocation_short_name = libgen::basename(*arg) };
234    }
235    // We check for NULL here since ld.so might already have initialized it for us, and we don't
236    // want to overwrite it if constructors in .init_array of dependency libraries have called
237    // setenv.
238    if unsafe { platform::environ }.is_null() {
239        // Set up envp
240        let envp = sp.envp();
241        let mut len = 0;
242        while !(unsafe { *envp.add(len) }).is_null() {
243            len += 1;
244        }
245        unsafe { platform::OUR_ENVIRON.unsafe_set(copy_string_array(envp, len)) };
246        unsafe { platform::environ = platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr() };
247    }
248
249    let auxvs = unsafe { get_auxvs(sp.auxv().cast()) };
250    unsafe { crate::platform::init(auxvs) };
251    init_array();
252    unsafe { crate::platform::logger::init() };
253
254    // Run preinit array
255    {
256        let mut f = core::ptr::from_ref(unsafe { &__preinit_array_start });
257        #[allow(clippy::op_ref)]
258        while f < &raw const __preinit_array_end {
259            (unsafe { *f })();
260            f = unsafe { f.offset(1) };
261        }
262    }
263
264    // Run init array
265    {
266        let mut f = core::ptr::from_ref(unsafe { &__init_array_start });
267        #[allow(clippy::op_ref)]
268        while f < &raw const __init_array_end {
269            (unsafe { *f })();
270            f = unsafe { f.offset(1) };
271        }
272    }
273
274    // not argv or envp, because programs like bash try to modify this *const* pointer :|
275    unsafe { stdlib::exit(main(argc, platform::argv, platform::environ)) };
276
277    unreachable!();
278}