Skip to main content

relibc/ld_so/
start.rs

1// Start code adapted from https://gitlab.redox-os.org/redox-os/relibc/blob/master/src/start.rs
2
3use core::slice;
4
5use alloc::{
6    borrow::ToOwned,
7    boxed::Box,
8    collections::BTreeMap,
9    string::{String, ToString},
10    vec::Vec,
11};
12use object::{
13    NativeEndian,
14    elf::{self, PT_DYNAMIC, PT_PHDR},
15    read::elf::{Dyn as _, ProgramHeader as _},
16};
17
18use crate::{
19    c_str::CStr,
20    header::{
21        elf::{AT_BASE, AT_ENTRY, AT_PHDR, AT_PHENT, AT_PHNUM},
22        unistd,
23    },
24    ld_so::{
25        dso::{
26            DT_RELR, DT_RELRENT, DT_RELRSZ, Dyn, ProgramHeader, Rel, Rela, Relocation,
27            RelocationKind, Relr, apply_relr,
28        },
29        linker::DebugFlags,
30    },
31    platform::{auxv_iter, get_auxvs, types::c_char},
32    start::Stack,
33    sync::mutex::Mutex,
34};
35
36use super::{
37    PATH_SEP,
38    access::accessible,
39    debug::_r_debug,
40    linker::{Config, Linker},
41    tcb::Tcb,
42};
43
44use generic_rt::ExpectTlsFree;
45
46#[cfg(target_pointer_width = "32")]
47pub const SIZEOF_EHDR: usize = 52;
48
49#[cfg(target_pointer_width = "64")]
50pub const SIZEOF_EHDR: usize = 64;
51
52unsafe fn get_argv(mut ptr: *const usize) -> (Vec<String>, *const usize) {
53    //traverse the stack and collect argument vector
54    let mut argv = Vec::new();
55    while unsafe { *ptr != 0 } {
56        let arg = unsafe { *ptr };
57        match unsafe { CStr::from_ptr(arg as *const c_char).to_str() } {
58            Ok(arg_str) => argv.push(arg_str.to_owned()),
59            _ => {
60                eprintln!("ld.so: failed to parse argv[{}]", argv.len());
61                unistd::_exit(1);
62            }
63        }
64        ptr = unsafe { ptr.add(1) };
65    }
66
67    (argv, ptr)
68}
69
70unsafe fn get_env(mut ptr: *const usize) -> (BTreeMap<String, String>, *const usize) {
71    //traverse the stack and collect argument environment variables
72    let mut envs = BTreeMap::new();
73    while unsafe { *ptr != 0 } {
74        let env = unsafe { *ptr };
75        if let Ok(arg_str) = unsafe { CStr::from_ptr(env as *const c_char).to_str() } {
76            let mut parts = arg_str.splitn(2, '=');
77            if let Some(key) = parts.next()
78                && let Some(value) = parts.next()
79            {
80                envs.insert(key.to_owned(), value.to_owned());
81            }
82        }
83        ptr = unsafe { ptr.add(1) };
84    }
85
86    (envs, ptr)
87}
88
89#[allow(unsafe_op_in_unsafe_fn)]
90unsafe fn adjust_stack(sp: &'static mut Stack) {
91    let mut argv = sp.argv() as *mut usize;
92
93    // Move arguments
94    loop {
95        let next_argv = argv.add(1);
96        let arg = *next_argv;
97        *argv = arg;
98        argv = next_argv;
99        if arg == 0 {
100            break;
101        }
102    }
103
104    // Move environment
105    loop {
106        let next_argv = argv.add(1);
107        let arg = *next_argv;
108        *argv = arg;
109        argv = next_argv;
110        if arg == 0 {
111            break;
112        }
113    }
114
115    // Move auxiliary vectors
116    loop {
117        let next_argv = argv.add(1);
118        let kind = *next_argv;
119        *argv = kind;
120        argv = next_argv;
121        let next_argv = argv.add(1);
122        let value = *next_argv;
123        *argv = value;
124        argv = next_argv;
125        if kind == 0 {
126            break;
127        }
128    }
129    sp.argc -= 1;
130}
131
132fn resolve_path_name(
133    name_or_path: &str,
134    envs: &BTreeMap<String, String>,
135) -> Option<(String, String)> {
136    if accessible(name_or_path, unistd::F_OK).is_ok() {
137        return Some((
138            name_or_path.to_string(),
139            name_or_path
140                .split("/")
141                .collect::<Vec<&str>>()
142                .last()
143                .unwrap()
144                .to_string(),
145        ));
146    }
147    if name_or_path.split("/").collect::<Vec<&str>>().len() != 1 {
148        return None;
149    }
150
151    let env_path = envs.get("PATH")?;
152    for part in env_path.split(PATH_SEP) {
153        let path = if part.is_empty() {
154            format!("./{}", name_or_path)
155        } else {
156            format!("{}/{}", part, name_or_path)
157        };
158        if accessible(&path, unistd::F_OK).is_ok() {
159            return Some((path.clone(), name_or_path.to_string()));
160        }
161    }
162    None
163}
164
165#[unsafe(no_mangle)]
166pub unsafe extern "C" fn relibc_ld_so_start(
167    sp: &'static mut Stack,
168    ld_entry: usize,
169    dynamic: *const Dyn,
170) -> usize {
171    // Relocate ourselves.
172    //
173    // This function is very delicate as it must **not** contain relocations itself. References to
174    // external symbols **cannot** be made until `stage2()` so, this function might not be very
175    // elegant.
176    //
177    // At this stage the TCB is not setup either so `expect_notls` must be used instead of `expect`
178    // and `unwrap`.
179    let mut at_phdr = None;
180    let mut at_phnum = None;
181    let mut at_phent = None;
182    let mut at_base = None;
183    let mut at_entry = None;
184    for [kind, value] in unsafe { auxv_iter(sp.auxv().cast::<usize>()) } {
185        match kind {
186            AT_PHDR => at_phdr = Some(value as *const ProgramHeader),
187            AT_PHNUM => at_phnum = Some(value),
188            AT_PHENT => at_phent = Some(value),
189            AT_BASE => at_base = Some(value),
190            AT_ENTRY => at_entry = Some(value),
191            _ => {}
192        }
193    }
194
195    let at_phdr = at_phdr.expect_notls("`AT_PHDR` must be present");
196    let at_phnum = at_phnum.expect_notls("`AT_PHNUM` must be present if `AT_PHDR` is");
197    let at_phent = at_phent.expect_notls("`AT_PHENT` must be present if `AT_PHDR` is");
198    assert!(!at_phdr.is_null() && at_phnum != 0 && at_phent == size_of::<ProgramHeader>());
199    let phdrs = unsafe { slice::from_raw_parts(at_phdr, at_phnum) };
200
201    let at_entry = at_entry.expect_notls("`AT_ENTRY` must be present");
202    let at_base = at_base.unwrap_or_default();
203
204    let self_base = if at_base != 0 {
205        at_base
206    } else {
207        let ph = phdrs
208            .iter()
209            .find(|ph| ph.p_type(NativeEndian) == PT_DYNAMIC)
210            .unwrap();
211        unsafe { dynamic.byte_sub(ph.p_vaddr(NativeEndian) as usize) as usize }
212    };
213
214    let is_manual = at_entry == ld_entry; // Whether the dynamic linker was invoked as a command.
215
216    let mut i = dynamic;
217    let mut rela_ptr = None;
218    let mut rela_len = None;
219    let mut relr_ptr = None;
220    let mut relr_len = None;
221    let mut rel_ptr = None;
222    let mut rel_len = None;
223    loop {
224        let entry = unsafe { &*i };
225        let val = entry.d_val(NativeEndian);
226        let ptr = val as *const u8;
227        match entry.d_tag(NativeEndian) as u32 {
228            elf::DT_NULL => break,
229            elf::DT_RELA => rela_ptr = Some(ptr.cast::<Rela>()),
230            elf::DT_RELASZ => rela_len = Some(val as usize / size_of::<Rela>()),
231            elf::DT_RELAENT => {
232                assert_eq!(val as usize, size_of::<Rela>(),);
233            }
234            elf::DT_REL => rel_ptr = Some(ptr.cast::<Rel>()),
235            elf::DT_RELSZ => rel_len = Some(val as usize / size_of::<Rel>()),
236            elf::DT_RELENT => {
237                assert_eq!(val as usize, size_of::<Rel>());
238            }
239            DT_RELR => relr_ptr = Some(ptr.cast::<Relr>()),
240            DT_RELRSZ => relr_len = Some(val as usize / size_of::<Relr>()),
241            DT_RELRENT => {
242                assert_eq!(val as usize, size_of::<Relr>());
243            }
244            _ => {}
245        }
246        i = unsafe { i.add(1) };
247    }
248
249    unsafe fn get_array<'a, T>(
250        ptr: Option<*const T>,
251        len: Option<usize>,
252        base_addr: usize,
253    ) -> &'a [T] {
254        if let Some(ptr) = ptr {
255            let len = len.expect_notls("dynamic entry was present without it's corresponding size");
256            unsafe { core::slice::from_raw_parts(ptr.byte_add(base_addr), len) }
257        } else {
258            &[]
259        }
260    }
261
262    fn do_relocs<'a, T>(relocs: &'a [T], self_base: usize)
263    where
264        Relocation: From<&'a T>,
265    {
266        for reloc in relocs {
267            let reloc: Relocation = reloc.into();
268            let ptr = (reloc.offset + self_base) as *mut usize;
269            if reloc.kind == RelocationKind::RELATIVE {
270                unsafe { *ptr = self_base + reloc.addend.unwrap_or_default() };
271            }
272        }
273    }
274
275    let rela = unsafe { get_array::<Rela>(rela_ptr, rela_len, self_base) };
276    let rel = unsafe { get_array::<Rel>(rel_ptr, rel_len, self_base) };
277    do_relocs(rela, self_base);
278    do_relocs(rel, self_base);
279
280    unsafe {
281        let relr = get_array(relr_ptr, relr_len, self_base);
282        apply_relr(self_base as *const u8, relr);
283    }
284
285    let mut base_addr = None;
286    if !is_manual {
287        // if we are not running in manual mode, then the main
288        // program is already loaded by the kernel and we want
289        // to use it. on redox, we treat it the same.
290        for ph in phdrs.iter() {
291            if ph.p_type(NativeEndian) == PT_PHDR {
292                assert!(base_addr.is_none(), "`PT_PHDR` cannot occur more than once");
293                base_addr = Some(unsafe {
294                    phdrs
295                        .as_ptr()
296                        .cast::<u8>()
297                        .sub(ph.p_vaddr(NativeEndian) as usize)
298                } as usize);
299            }
300        }
301    }
302
303    stage2(sp, self_base, is_manual, base_addr)
304}
305
306fn stage2(
307    sp: &'static mut Stack,
308    self_base: usize,
309    is_manual: bool,
310    base_addr: Option<usize>,
311) -> usize {
312    // Setup TCB for ourselves.
313    unsafe {
314        #[cfg(target_os = "redox")]
315        let auxv = sp.auxv().cast();
316        #[cfg(target_os = "redox")]
317        let thr_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_THR_FD)
318            .expect_notls("no thread fd present");
319
320        #[cfg(target_os = "redox")]
321        {
322            if redox_rt::current_filetable().fd().is_none() {
323                let filetable_fd = crate::platform::get_auxv_raw(
324                    sp.auxv().cast(),
325                    redox_rt::auxv_defs::AT_REDOX_FILETABLE_FD,
326                )
327                .expect_notls("no filetable fd present");
328                let filetable_guard = redox_rt::proc::FdGuard::new(filetable_fd)
329                    .to_upper()
330                    .expect_notls("failed to move filetable fd to upper table");
331                *redox_rt::current_filetable() =
332                    redox_rt::sys::FdTbl::from_binary_fd(filetable_guard)
333                        .expect_notls("failed to initialize FILETABLE");
334            }
335        }
336
337        let tcb = Tcb::new(0).expect_notls("[ld.so]: failed to allocate bootstrap TCB");
338        tcb.activate(
339            #[cfg(target_os = "redox")]
340            Some(
341                redox_rt::proc::FdGuard::new(thr_fd)
342                    .to_upper()
343                    .expect_notls("failed to move thread fd to upper table"),
344            ),
345        );
346        #[cfg(target_os = "redox")]
347        {
348            let proc_fd =
349                crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_PROC_FD)
350                    .expect_notls("no proc fd present");
351
352            let ns_fd = crate::platform::get_auxv_raw(auxv, redox_rt::auxv_defs::AT_REDOX_NS_FD)
353                .filter(|&fd| fd != usize::MAX)
354                .map(|fd| {
355                    redox_rt::proc::FdGuard::new(fd)
356                        .to_upper()
357                        .expect_notls("failed to move ns fd to upper table")
358                });
359
360            redox_rt::initialize(
361                redox_rt::proc::FdGuard::new(proc_fd)
362                    .to_upper()
363                    .expect_notls("failed to move proc fd to upper table"),
364                ns_fd,
365            );
366            redox_rt::signal::setup_sighandler(&tcb.os_specific, true);
367        }
368    }
369
370    // We get the arguments, the environment, and the auxilary vector
371    let (argv, envs, auxv) = unsafe {
372        let argv_start = sp.argv() as *mut usize;
373        let (argv, argv_end) = get_argv(argv_start);
374        let (envs, envs_end) = get_env(argv_end.add(1));
375        let auxv = get_auxvs(envs_end.add(1));
376        (argv, envs, auxv)
377    };
378
379    unsafe {
380        crate::platform::OUR_ENVIRON.unsafe_set(
381            envs.iter()
382                .map(|(k, v)| {
383                    let mut var = Vec::with_capacity(k.len() + v.len() + 2);
384                    var.extend(k.as_bytes());
385                    var.push(b'=');
386                    var.extend(v.as_bytes());
387                    var.push(b'\0');
388                    let mut var = var.into_boxed_slice();
389                    let ptr = var.as_mut_ptr();
390                    core::mem::forget(var);
391                    ptr.cast()
392                })
393                .chain(core::iter::once(core::ptr::null_mut()))
394                .collect::<Vec<_>>(),
395        );
396
397        crate::platform::environ = crate::platform::OUR_ENVIRON.unsafe_mut().as_mut_ptr();
398    }
399
400    // we might need global lock for this kind of stuff
401    _r_debug.lock().r_ldbase = self_base;
402
403    let name_or_path = if is_manual {
404        // ld.so is run directly by user and not via execve() or similar systemcall
405        println!("argv: {:#?}", argv);
406        println!("envs: {:#?}", envs);
407        println!("auxv: {:#x?}", auxv);
408
409        if sp.argc < 2 {
410            eprintln!("ld.so [executable] [arguments...]");
411            unistd::_exit(1);
412        }
413        unsafe { adjust_stack(sp) };
414        argv[1].clone()
415    } else {
416        argv[0].clone()
417    };
418
419    // TODO: Fix memory leak, although minimal.
420    #[cfg(target_os = "redox")]
421    unsafe {
422        crate::platform::init_inner(auxv);
423    }
424
425    let (path, _name) = match resolve_path_name(&name_or_path, &envs) {
426        Some((p, n)) => (p, n),
427        None => {
428            eprintln!("[ld.so]: failed to locate '{name_or_path}'");
429            unistd::_exit(1);
430        }
431    };
432
433    let config = Config::from_env(&envs);
434    if config.debug_flags.contains(DebugFlags::LOAD) {
435        println!("[ld.so]: relocated self at {self_base:#x}!");
436        if let Some(base_addr) = base_addr {
437            println!("[ld.so]: executable has been already loaded at {base_addr:#x?}");
438        }
439    }
440
441    let mut linker = Linker::new(config);
442    let entry = match linker.load_program(&path, base_addr) {
443        Ok(entry) => entry,
444        Err(err) => {
445            eprintln!("[ld.so]: failed to link '{path}': {err:?}");
446            eprintln!("[ld.so]: enable debug output with `LD_DEBUG=all` for more information");
447            unistd::_exit(1);
448        }
449    };
450    if let Some(tcb) = unsafe { Tcb::current() } {
451        tcb.linker_ptr = Box::into_raw(Box::new(Mutex::new(linker)));
452    }
453    if is_manual {
454        eprintln!("[ld.so]: entry '{path}': {entry:#x}");
455    }
456    entry
457}