Skip to main content

relibc/ld_so/
linker.rs

1use alloc::{
2    collections::BTreeMap,
3    rc::Rc,
4    string::{String, ToString},
5    sync::{Arc, Weak},
6    vec::Vec,
7};
8use object::elf;
9#[cfg(not(target_arch = "x86"))]
10use object::{
11    NativeEndian,
12    read::elf::{Rela as _, Sym},
13};
14
15use core::{
16    cell::RefCell,
17    ptr::{self, NonNull},
18};
19
20use crate::{
21    ALLOCATOR,
22    c_str::{CStr, CString},
23    error::Errno,
24    header::{
25        dl_tls::{__tls_get_addr, dl_tls_index},
26        fcntl, sys_mman,
27        unistd::F_OK,
28    },
29    ld_so::dso::SymbolBinding,
30    out::Out,
31    platform::{
32        Pal, Sys,
33        types::{c_int, c_void},
34    },
35    sync::rwlock::RwLock,
36};
37
38#[cfg(not(target_arch = "x86"))]
39use crate::{ld_so::dso::resolve_sym, platform::types::c_uint};
40
41#[cfg(not(target_arch = "x86"))]
42use super::dso::Rela;
43use super::{
44    PATH_SEP,
45    access::accessible,
46    callbacks::LinkerCallbacks,
47    debug::{_dl_debug_state, _r_debug, RTLDState},
48    dso::{DSO, ProgramHeader},
49    tcb::{Master, Tcb},
50};
51
52#[derive(Debug, Copy, Clone)]
53pub enum DlError {
54    /// Failed to locate the requested DSO.
55    NotFound,
56    /// The DSO is malformed somehow.
57    Malformed,
58    /// Invalid DSO handle.
59    InvalidHandle,
60    /// Out of memory.
61    Oom,
62}
63
64impl DlError {
65    /// Returns a human-readable, null-terminated C string describing the error.
66    pub const fn repr(&self) -> &'static core::ffi::CStr {
67        match self {
68            DlError::NotFound => {
69                c"Failed to locate the requested DSO. Set `LD_DEBUG=all` for more information."
70            }
71
72            DlError::Malformed => {
73                c"The DSO is malformed somehow. Set `LD_DEBUG=all` for more information."
74            }
75
76            DlError::InvalidHandle => {
77                c"Invalid DSO handle. Set `LD_DEBUG=all` for more information."
78            }
79
80            DlError::Oom => c"Out of memory.",
81        }
82    }
83}
84
85pub type Result<T> = core::result::Result<T, DlError>;
86
87pub(super) static GLOBAL_SCOPE: RwLock<Scope> = RwLock::new(Scope::global());
88
89struct MmapFile {
90    fd: i32,
91    ptr: *mut c_void,
92    size: usize,
93}
94
95impl MmapFile {
96    fn open(path: CStr, oflag: c_int) -> core::result::Result<Self, Errno> {
97        let fd = Sys::open(path, oflag, 0 /* mode */)?;
98        let mut stat = crate::header::sys_stat::stat::default();
99        Sys::fstat(fd, Out::from_mut(&mut stat))?;
100
101        Self::from_fd(fd, stat.st_size as usize)
102    }
103
104    fn from_fd(fd: i32, size: usize) -> core::result::Result<Self, Errno> {
105        let ptr = unsafe {
106            Sys::mmap(
107                ptr::null_mut(),
108                size,
109                sys_mman::PROT_READ,
110                sys_mman::MAP_PRIVATE,
111                fd,
112                0,
113            )
114        }?;
115
116        Ok(Self { fd, ptr, size })
117    }
118
119    fn anonymous(size: usize) -> core::result::Result<Self, Errno> {
120        let ptr = unsafe {
121            Sys::mmap(
122                ptr::null_mut(),
123                size,
124                sys_mman::PROT_READ | sys_mman::PROT_WRITE,
125                sys_mman::MAP_PRIVATE | sys_mman::MAP_ANONYMOUS,
126                -1,
127                0,
128            )
129        }?;
130
131        Ok(Self { fd: -1, ptr, size })
132    }
133
134    fn data(&self) -> &[u8] {
135        unsafe { core::slice::from_raw_parts(self.ptr.cast::<u8>(), self.size) }
136    }
137
138    fn as_mut_slice(&self) -> &mut [u8] {
139        unsafe { core::slice::from_raw_parts_mut(self.ptr.cast::<u8>(), self.size) }
140    }
141}
142
143impl Drop for MmapFile {
144    fn drop(&mut self) {
145        unsafe {
146            Sys::munmap(self.ptr, self.size).unwrap();
147            if self.fd != -1 {
148                Sys::close(self.fd).unwrap();
149            }
150        }
151    }
152}
153
154#[derive(Clone, Debug)]
155pub struct Symbol<'a> {
156    pub name: &'a str,
157    pub value: usize,
158    pub base: usize,
159    pub size: usize,
160    pub sym_type: u8,
161}
162
163impl Symbol<'_> {
164    pub fn as_ptr(&self) -> *mut c_void {
165        (self.base + self.value) as *mut c_void
166    }
167}
168
169#[derive(Debug, Default, Copy, Clone, PartialEq)]
170pub enum Resolve {
171    /// Resolve all undefined symbols immediately.
172    #[cfg_attr(not(target_arch = "x86_64"), default)]
173    Now,
174    /// Perform lazy binding (i.e. symbols will be resolved when they are first
175    /// used).
176    #[cfg_attr(target_arch = "x86_64", default)]
177    Lazy,
178}
179
180#[derive(Debug, Copy, Clone, PartialEq)]
181pub enum ScopeKind {
182    Global,
183    Local,
184}
185
186pub enum Scope {
187    /// The global scope initially contains the main program and all of its
188    /// dependencies. Additional objects will be added to this scope via
189    /// `dlopen(2)` if the `RTLD_GLOBAL` flag is set.
190    Global { objs: Vec<Weak<DSO>> },
191    Local {
192        owner: Option<Weak<DSO>>,
193        objs: Vec<Arc<DSO>>,
194    },
195}
196
197impl Scope {
198    #[inline]
199    const fn global() -> Self {
200        Self::Global { objs: Vec::new() }
201    }
202
203    #[inline]
204    const fn local() -> Self {
205        Self::Local {
206            owner: None,
207            objs: Vec::new(),
208        }
209    }
210
211    fn set_owner(&mut self, obj: Weak<DSO>) {
212        match self {
213            Self::Global { .. } => panic!("attempted to set global scope owner"),
214            Self::Local { owner, .. } => {
215                assert!(owner.is_none(), "attempted to change local scope owner");
216                *owner = Some(obj);
217            }
218        }
219    }
220
221    fn add(&mut self, target: &Arc<DSO>) {
222        match self {
223            Self::Global { objs } => {
224                let target = Arc::downgrade(target);
225                for obj in objs.iter() {
226                    if Weak::ptr_eq(obj, &target) {
227                        return;
228                    }
229                }
230
231                objs.push(target);
232            }
233
234            Self::Local { objs, .. } => {
235                for obj in objs.iter() {
236                    if Arc::ptr_eq(obj, target) {
237                        return;
238                    }
239                }
240
241                objs.push(target.clone());
242            }
243        }
244    }
245
246    pub(super) fn get_sym<'a>(
247        &self,
248        name: &'a str,
249    ) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
250        self._get_sym(name, 0)
251    }
252
253    pub(super) fn _get_sym<'a>(
254        &self,
255        name: &'a str,
256        skip: usize,
257    ) -> Option<(Symbol<'a>, SymbolBinding, Arc<DSO>)> {
258        let mut res = None;
259
260        let get_sym = |obj: Arc<DSO>| {
261            if let Some((sym, binding)) = obj.get_sym(name) {
262                if binding.is_global() {
263                    return Some((sym, binding, obj.clone()));
264                }
265
266                res = Some((sym, binding, obj.clone()));
267            }
268
269            None
270        };
271
272        match self {
273            Self::Global { objs } => objs
274                .iter()
275                .skip(skip)
276                .map(|o| o.upgrade().unwrap())
277                .find_map(get_sym),
278            Self::Local { owner, objs } => {
279                let owner = owner
280                    .as_ref()
281                    .expect("local scope without owner")
282                    .upgrade()
283                    .expect("local scope owner was dropped");
284
285                core::iter::once(owner)
286                    .chain(objs.iter().cloned())
287                    .skip(skip)
288                    .find_map(get_sym)
289            }
290        }
291        .or(res)
292    }
293
294    fn copy_into(&self, other: &mut Self) {
295        match (self, other) {
296            (Self::Local { owner, objs }, Self::Global { objs: other_objs }) => {
297                // FIXME: may have duplicates
298                let owner = owner.as_ref().expect("local scope without owner");
299                other_objs.push(owner.clone());
300                other_objs.extend(objs.iter().map(Arc::downgrade));
301            }
302
303            _ => unreachable!(),
304        }
305    }
306
307    fn debug(&self) {
308        match self {
309            Self::Global { objs } => {
310                println!(
311                    "[@global] {:?}",
312                    objs.iter()
313                        .map(|x| x.upgrade().unwrap().name.clone())
314                        .collect::<Vec<_>>()
315                );
316            }
317
318            Self::Local { owner, objs } => {
319                let owner = owner.as_ref().unwrap().upgrade().unwrap();
320                println!(
321                    "[{}] {:?}",
322                    owner.name,
323                    objs.iter().map(|x| x.name.clone()).collect::<Vec<_>>()
324                )
325            }
326        }
327    }
328}
329
330// Used by dlfcn.h
331//
332// We need this as the handle must be created and destroyed with the dynamic
333// linker's allocator.
334pub struct ObjectHandle(*const DSO);
335
336impl ObjectHandle {
337    #[inline]
338    fn new(obj: Arc<DSO>) -> Self {
339        Self(Arc::into_raw(obj))
340    }
341
342    #[inline]
343    fn into_inner(self) -> Arc<DSO> {
344        unsafe { Arc::from_raw(self.0) }
345    }
346
347    #[inline]
348    pub fn as_ptr(&self) -> *const c_void {
349        self.0.cast()
350    }
351
352    #[inline]
353    pub fn from_ptr(ptr: *const c_void) -> Option<Self> {
354        NonNull::new(ptr as *mut DSO).map(|ptr| Self(ptr.as_ptr()))
355    }
356}
357
358impl AsRef<DSO> for ObjectHandle {
359    #[inline]
360    fn as_ref(&self) -> &DSO {
361        unsafe { &*self.0 }
362    }
363}
364
365bitflags::bitflags! {
366    #[derive(Debug, Default)]
367    pub struct DebugFlags: u32 {
368        /// Display what objects and where they are being loaded.
369        const LOAD = 1 << 1;
370        /// Display library search paths.
371        const SEARCH = 1 << 2;
372        /// Display scope information.
373        const SCOPES = 1 << 3;
374    }
375}
376
377#[derive(Default)]
378pub struct Config {
379    pub debug_flags: DebugFlags,
380    library_path: Option<String>,
381    /// Resolve symbols at program startup.
382    bind_now: bool,
383}
384
385impl Config {
386    pub fn from_env(env: &BTreeMap<String, String>) -> Self {
387        let debug_flags = env
388            .get("LD_DEBUG")
389            .map(|value| {
390                let mut flags = DebugFlags::empty();
391                for opt in value.split(',') {
392                    flags |= match opt {
393                        "load" => DebugFlags::LOAD,
394                        "search" => DebugFlags::SEARCH,
395                        "scopes" => DebugFlags::SCOPES,
396                        "all" => DebugFlags::all(),
397                        _ => {
398                            eprintln!("[ld.so]: unknown debug flag '{}'", opt);
399                            DebugFlags::empty()
400                        }
401                    };
402                }
403
404                flags
405            })
406            .unwrap_or(DebugFlags::empty());
407
408        Self {
409            debug_flags,
410            library_path: env.get("LD_LIBRARY_PATH").cloned(),
411            bind_now: env
412                .get("LD_BIND_NOW")
413                .map(|value| !value.is_empty())
414                .unwrap_or_default(),
415        }
416    }
417}
418
419pub struct Linker {
420    config: Config,
421
422    next_object_id: usize,
423    next_tls_module_id: usize,
424    tls_size: usize,
425    objects: BTreeMap<usize, Arc<DSO>>,
426    name_to_object_id_map: BTreeMap<String, usize>,
427    pub cbs: Rc<RefCell<LinkerCallbacks>>,
428}
429
430const ROOT_ID: usize = 1;
431
432impl Linker {
433    pub fn new(config: Config) -> Self {
434        Self {
435            config,
436            next_object_id: ROOT_ID,
437            next_tls_module_id: 1,
438            tls_size: 0,
439            objects: BTreeMap::new(),
440            name_to_object_id_map: BTreeMap::new(),
441            cbs: Rc::new(RefCell::new(LinkerCallbacks::new())),
442        }
443    }
444
445    pub fn load_program(&mut self, path: &str, base_addr: Option<usize>) -> Result<usize> {
446        let dso = self.load_object(
447            path,
448            &None,
449            base_addr,
450            false,
451            if self.config.bind_now {
452                Resolve::Now
453            } else {
454                Resolve::default()
455            },
456            ScopeKind::Global,
457        )?;
458        Ok(dso.entry_point)
459    }
460
461    pub fn load_library(
462        &mut self,
463        name: Option<&str>,
464        resolve: Resolve,
465        scope: ScopeKind,
466        noload: bool,
467    ) -> Result<ObjectHandle> {
468        log::trace!(
469            "[ld.so] load_library(name={:?}, resolve={:#?}, scope={:#?}, noload={})",
470            name,
471            resolve,
472            scope,
473            noload
474        );
475
476        if noload && resolve == Resolve::Now {
477            // Do not perform lazy binding anymore.
478            // * Check if loaded with Resolve::Now and if so, early return.
479            // * If not, resolve all symbols now.
480            todo!("resolve symbols now!");
481        }
482
483        match name {
484            Some(name) => {
485                if let Some(id) = self.name_to_object_id_map.get(name) {
486                    let obj = self.objects.get(id).unwrap();
487
488                    // We may be upgrading the object from a local scope to the
489                    // global scope.
490                    if scope == ScopeKind::Global {
491                        if self.config.debug_flags.contains(DebugFlags::SCOPES) {
492                            eprintln!("[ld.so]: moving {} into the global scope", obj.name);
493                        }
494
495                        {
496                            let mut global_scope = GLOBAL_SCOPE.write();
497                            obj.scope().copy_into(&mut global_scope);
498                        }
499                        self.scope_debug();
500                    }
501
502                    Ok(ObjectHandle::new(obj.clone()))
503                } else if !noload {
504                    let parent_runpath = &self
505                        .objects
506                        .get(&ROOT_ID)
507                        .and_then(|parent| parent.runpath().cloned());
508
509                    Ok(ObjectHandle::new(self.load_object(
510                        name,
511                        parent_runpath,
512                        None,
513                        true,
514                        if self.config.bind_now {
515                            Resolve::Now
516                        } else {
517                            resolve
518                        },
519                        scope,
520                    )?))
521                } else {
522                    // FIXME: LoadError?
523                    // Err(Error::Malformed(format!(
524                    //     "object '{}' has not yet been loaded",
525                    //     name
526                    // )))
527                    Ok(ObjectHandle(ptr::null()))
528                }
529            }
530
531            None => match self.objects.get(&ROOT_ID) {
532                Some(obj) => Ok(ObjectHandle::new(obj.clone())),
533                None => Err(DlError::NotFound),
534            },
535        }
536    }
537
538    pub fn get_sym(&self, handle: Option<ObjectHandle>, name: &str) -> Option<*mut c_void> {
539        let guard;
540
541        if let Some(handle) = handle.as_ref() {
542            handle.as_ref().scope()
543        } else {
544            guard = GLOBAL_SCOPE.read();
545            &guard
546        }
547        .get_sym(name)
548        .map(|(symbol, _, obj)| {
549            if symbol.sym_type != elf::STT_TLS {
550                symbol.as_ptr()
551            } else {
552                let mut tls_index = dl_tls_index {
553                    ti_module: obj.tls_module_id,
554                    ti_offset: symbol.value,
555                };
556
557                unsafe { __tls_get_addr(&raw mut tls_index) }
558            }
559        })
560    }
561
562    pub fn unload(&mut self, handle: ObjectHandle) {
563        let obj = handle.into_inner();
564        if !obj.dlopened {
565            return;
566        }
567
568        log::trace!(
569            "[ld.so] unloading {} (sc={}, wc={})",
570            obj.name,
571            Arc::strong_count(&obj),
572            Arc::weak_count(&obj)
573        );
574
575        // One for the reference we have and the other for the one in the
576        // objects map.
577        if Arc::strong_count(&obj) == 2 {
578            // Remove from the global scope.
579            match *GLOBAL_SCOPE.write() {
580                Scope::Global { ref mut objs } => {
581                    objs.retain(|o| !Weak::ptr_eq(o, &Arc::downgrade(&obj)));
582                }
583
584                _ => unreachable!(),
585            }
586
587            let _ = self.objects.remove(&obj.id).unwrap();
588            for dep in obj.dependencies() {
589                if let Some(name) = self.name_to_object_id_map.get(*dep)
590                    && let Some(object_name) = self.objects.get(name)
591                {
592                    self.unload(ObjectHandle::new(object_name.clone()));
593                }
594            }
595            self.name_to_object_id_map.remove(&obj.name);
596            assert!(Arc::strong_count(&obj) == 1);
597            drop(obj);
598        }
599
600        // obj is dropped here.
601    }
602
603    pub fn fini(&self) {
604        for obj in self.objects.values() {
605            obj.run_fini();
606        }
607    }
608
609    fn load_object(
610        &mut self,
611        path: &str,
612        runpath: &Option<String>,
613        base_addr: Option<usize>,
614        dlopened: bool,
615        resolve: Resolve,
616        scope: ScopeKind,
617    ) -> Result<Arc<DSO>> {
618        let resolve = if cfg!(target_arch = "x86_64") {
619            resolve
620        } else {
621            // Lazy binding is not currently supported on non-x86_64 architectures.
622            Resolve::Now
623        };
624
625        _r_debug.lock().state = RTLDState::RtAdd;
626        _dl_debug_state();
627
628        let mut new_objects = Vec::new();
629        let mut objects_data = Vec::new();
630        let mut tcb_masters = Vec::new();
631        let loaded_dso = self.load_objects_recursive(
632            path,
633            runpath,
634            base_addr,
635            dlopened,
636            &mut new_objects,
637            &mut objects_data,
638            &mut tcb_masters,
639            None,
640            scope,
641        )?;
642
643        for (i, obj) in new_objects.iter().enumerate() {
644            obj.relocate(&objects_data[i], resolve).unwrap();
645        }
646
647        unsafe {
648            if !dlopened {
649                #[cfg(target_os = "redox")]
650                let (tcb, old_tcb, thr_fd) = {
651                    use redox_rt::signal::tmp_disable_signals;
652
653                    let old_tcb = Tcb::current().expect("failed to get bootstrap TCB");
654                    let thr_fd = (&mut *old_tcb.os_specific.thr_fd.get())
655                        .take()
656                        .expect("no thread FD present");
657                    let new_tcb = Tcb::new(self.tls_size)?; // This actually allocates TCB, TLS and ABI page.
658
659                    // Stash
660                    let new_tls_end = new_tcb.generic.tls_end;
661                    let new_tls_len = new_tcb.generic.tls_len;
662                    let new_tcb_ptr = new_tcb.generic.tcb_ptr;
663                    let new_tcb_len = new_tcb.generic.tcb_len;
664
665                    // Unmap just the TCB page.
666                    Sys::munmap(new_tcb as *mut Tcb as *mut c_void, syscall::PAGE_SIZE).unwrap();
667
668                    let new_addr = ptr::addr_of!(*new_tcb) as usize;
669
670                    assert_eq!(
671                        syscall::syscall5(
672                            syscall::SYS_MREMAP,
673                            old_tcb as *mut Tcb as usize,
674                            syscall::PAGE_SIZE,
675                            new_addr,
676                            syscall::PAGE_SIZE,
677                            (syscall::MremapFlags::FIXED | syscall::MremapFlags::KEEP_OLD).bits()
678                                | (syscall::MapFlags::PROT_READ | syscall::MapFlags::PROT_WRITE)
679                                    .bits(),
680                        )
681                        .expect("mremap: failed to alias TCB"),
682                        new_addr,
683                    );
684                    // XXX: New TCB is now at the same physical address as the old TCB.
685
686                    let _guard = tmp_disable_signals();
687                    // Restore
688                    new_tcb.generic.tls_end = new_tls_end;
689                    new_tcb.generic.tls_len = new_tls_len;
690                    new_tcb.generic.tcb_ptr = new_tcb_ptr;
691                    new_tcb.generic.tcb_len = new_tcb_len;
692
693                    drop(_guard);
694                    (new_tcb, old_tcb as *mut Tcb as *mut c_void, thr_fd)
695                };
696
697                #[cfg(not(target_os = "redox"))]
698                let tcb = Tcb::new(self.tls_size)?;
699
700                // We are now loading the main program or its dependencies. The TLS for all initially
701                // loaded objects reside in the static TLS block. Depending on the architecture, the
702                // static TLS block is either placed before the TP or after the TP.
703                //
704                // Setup the DTVs.
705                tcb.setup_dtv(tcb_masters.len());
706
707                for obj in new_objects.iter() {
708                    if obj.tls_module_id == 0 {
709                        // No TLS for this object.
710                        continue;
711                    }
712
713                    let dtv_idx = obj.tls_module_id - 1;
714
715                    if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
716                        // Below the TP
717                        tcb.dtv_mut()[dtv_idx] = tcb.tls_end.sub(obj.tls_offset);
718                    } else {
719                        // FIMXE(andypython): Make it above the TP
720                        //
721                        // tcb.dtv_mut().unwrap()[obj.tls_module_id - 1] =
722                        //     tcb_ptr.add(1).cast::<u8>().add(obj.tls_offset);
723                        //
724                        // FIXME(andypython): https://gitlab.redox-os.org/redox-os/relibc/-/merge_requests/570#note_35788
725                        let tls_start = tcb.tls_end.sub(tcb.tls_len);
726                        tcb.dtv_mut()[dtv_idx] = tls_start.add(obj.tls_offset);
727                    }
728                }
729
730                tcb.append_masters(tcb_masters);
731                // Copy the master data into the static TLS block.
732                tcb.copy_masters().map_err(|_| DlError::Malformed)?;
733                tcb.activate(
734                    #[cfg(target_os = "redox")]
735                    Some(thr_fd),
736                );
737                tcb.mspace = ALLOCATOR.get();
738
739                #[cfg(target_os = "redox")]
740                {
741                    // Unmap the old TCB.
742                    Sys::munmap(old_tcb, syscall::PAGE_SIZE).unwrap();
743                }
744            } else {
745                let tcb = Tcb::current().expect("failed to get current tcb");
746
747                // TLS variables for dlopen'ed objects are lazily allocated in `__tls_get_addr`.
748                tcb.append_masters(tcb_masters);
749            }
750        }
751
752        for obj in new_objects.into_iter() {
753            obj.mark_ready();
754            self.run_init(&obj);
755            self.register_object(obj);
756        }
757
758        _r_debug.lock().state = RTLDState::RtConsistent;
759        _dl_debug_state();
760
761        Ok(loaded_dso)
762    }
763
764    fn register_object(&mut self, obj: Arc<DSO>) {
765        self.name_to_object_id_map.insert(obj.name.clone(), obj.id);
766        self.objects.insert(obj.id, obj);
767    }
768
769    /// Loads the specified object and all of its dependencies.
770    ///
771    /// `new_objects` contains any new objects that were loaded. Order is
772    /// reverse of how the scope is populated.
773    ///
774    /// The scope is populated such that the loaded objects are in breadth-first
775    /// order. This means that first the requested object is added to the scope,
776    /// and then its dependencies are added in the order of their respective
777    /// `DT_NEEDED` entries in the requested object. This is done recursively
778    /// until all dependencies have been loaded.
779    ///
780    /// If a dependency has already been loaded, it is *not* added to the scope
781    /// nor to `new_objects`.
782    #[allow(clippy::too_many_arguments)]
783    fn load_objects_recursive(
784        &mut self,
785        name: &str,
786        parent_runpath: &Option<String>,
787        base_addr: Option<usize>,
788        dlopened: bool,
789        new_objects: &mut Vec<Arc<DSO>>,
790        objects_data: &mut Vec<Vec<ProgramHeader>>,
791        tcb_masters: &mut Vec<Master>,
792        // Scope of the object that caused this object to be loaded.
793        dependent_scope: Option<&mut Scope>,
794        scope_kind: ScopeKind,
795    ) -> Result<Arc<DSO>> {
796        // fixme: double lookup slow
797        if let Some(id) = self.name_to_object_id_map.get(name) {
798            if let Some(obj) = self.objects.get(id) {
799                if let Some(scope) = dependent_scope {
800                    match scope_kind {
801                        ScopeKind::Local => scope.add(obj),
802                        ScopeKind::Global => GLOBAL_SCOPE.write().add(obj),
803                    }
804                } else if scope_kind == ScopeKind::Global {
805                    GLOBAL_SCOPE.write().add(obj);
806                }
807                return Ok(obj.clone());
808            }
809        } else if let Some(obj) = new_objects.iter().find(|o| o.name == name) {
810            if let Some(scope) = dependent_scope {
811                match scope_kind {
812                    ScopeKind::Local => scope.add(obj),
813                    ScopeKind::Global => GLOBAL_SCOPE.write().add(obj),
814                }
815            } else if scope_kind == ScopeKind::Global {
816                GLOBAL_SCOPE.write().add(obj);
817            }
818            return Ok(obj.clone());
819        }
820
821        let debug = self.config.debug_flags.contains(DebugFlags::LOAD);
822
823        let path = self.search_object(name, parent_runpath)?;
824        let file = self.read_file(&path)?;
825        let data = file.data();
826        let (obj, tcb_master, elf) = DSO::new(
827            &path,
828            data,
829            base_addr,
830            dlopened,
831            self.next_object_id,
832            self.next_tls_module_id,
833            // Ensure TLS is aligned to 16 bytes for SSE
834            self.tls_size.next_multiple_of(16),
835        )
836        .map_err(|err| {
837            if debug {
838                eprintln!("[ld.so]: failed to load '{}': {}", name, err)
839            }
840
841            DlError::Malformed
842        })?;
843
844        if debug {
845            eprintln!(
846                "[ld.so]: loading object: {} at {:#x}:{:#x} (pie: {})",
847                name,
848                obj.mmap.as_ptr() as usize,
849                obj.mmap.as_ptr() as usize + obj.mmap.len(),
850                obj.pie,
851            );
852        }
853
854        self.next_object_id += 1;
855
856        if let Some(master) = tcb_master {
857            if !dlopened {
858                self.tls_size = master.offset; // => aligned ph.p_memsz
859            }
860
861            tcb_masters.push(master);
862            self.next_tls_module_id += 1;
863        }
864
865        let runpath = obj.runpath().cloned();
866        let dependencies = obj
867            .dependencies()
868            .iter()
869            .map(|dep| dep.to_string())
870            .collect::<Vec<_>>();
871
872        let obj = Arc::new(obj);
873        let mut scope = Scope::local();
874
875        if let Some(dependent_scope) = dependent_scope {
876            match scope_kind {
877                ScopeKind::Local => dependent_scope.add(&obj),
878                ScopeKind::Global => GLOBAL_SCOPE.write().add(&obj),
879            }
880        } else if let ScopeKind::Global = scope_kind {
881            GLOBAL_SCOPE.write().add(&obj);
882        }
883
884        for dep_name in dependencies.iter() {
885            self.load_objects_recursive(
886                dep_name,
887                &runpath,
888                None,
889                dlopened,
890                new_objects,
891                objects_data,
892                tcb_masters,
893                Some(&mut scope),
894                scope_kind,
895            )?;
896        }
897
898        objects_data.push(elf);
899        new_objects.push(obj.clone());
900
901        scope.set_owner(Arc::downgrade(&obj));
902        obj.scope.call_once(|| scope);
903
904        Ok(obj)
905    }
906
907    fn search_object(&self, name: &str, parent_runpath: &Option<String>) -> Result<String> {
908        let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
909        if debug {
910            eprintln!("[ld.so]: looking for '{}'", name);
911        }
912
913        let mut full_path = name.to_string();
914        if accessible(&full_path, F_OK).is_ok() {
915            if debug {
916                eprintln!("[ld.so]: found at '{}'!", full_path);
917            }
918            return Ok(full_path);
919        } else {
920            let mut search_paths = Vec::new();
921            if let Some(runpath) = parent_runpath {
922                search_paths.extend(runpath.split(PATH_SEP));
923            }
924            if let Some(ld_path) = self.config.library_path.as_ref() {
925                search_paths.extend(ld_path.split(PATH_SEP));
926            }
927            search_paths.push("/lib");
928            for part in search_paths.iter() {
929                full_path = format!("{}/{}", part, name);
930                if debug {
931                    eprintln!("[ld.so]: trying path '{}'", full_path);
932                }
933                if accessible(&full_path, F_OK).is_ok() {
934                    if debug {
935                        eprintln!("[ld.so]: found at '{}'!", full_path);
936                    }
937                    return Ok(full_path);
938                }
939            }
940        }
941
942        if debug {
943            eprintln!("[ld.so]: failed to locate '{}'", name);
944        }
945
946        Err(DlError::NotFound)
947    }
948
949    fn read_file(&self, path: &str) -> Result<MmapFile> {
950        let debug = self.config.debug_flags.contains(DebugFlags::SEARCH);
951
952        let path_c = CString::new(path).map_err(|err| {
953            if debug {
954                eprintln!("[ld.so]: invalid path '{}': {}", path, err)
955            }
956
957            DlError::NotFound
958        })?;
959
960        let file = {
961            let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC;
962            MmapFile::open(CStr::borrow(&path_c), flags).map_err(|err| {
963                if debug {
964                    eprintln!("[ld.so]: failed to open '{}': {}", path, err)
965                }
966
967                DlError::NotFound
968            })?
969        };
970
971        Ok(file)
972    }
973
974    fn run_init(&self, obj: &DSO) {
975        use crate::platform::{self, types::*};
976
977        if let Some((symbol, SymbolBinding::Global)) = obj.get_sym("__relibc_init_environ") {
978            unsafe {
979                symbol
980                    .as_ptr()
981                    .cast::<*mut *mut c_char>()
982                    .write(platform::environ);
983            }
984        }
985
986        obj.run_init();
987    }
988
989    fn scope_debug(&self) {
990        if self.config.debug_flags.contains(DebugFlags::SCOPES) {
991            println!("[ld.so]: =========== SCOPES ==========");
992            GLOBAL_SCOPE.read().debug();
993            for obj in self.objects.values() {
994                obj.scope().debug();
995            }
996            println!("[ld.so]: ==============================");
997        }
998    }
999}
1000
1001// GOT[1] = object_id
1002// GOT[2] = __plt_resolve_trampoline
1003//
1004// The stubs in .plt will push the relocation index and the object pointer onto
1005// the stack and jump to [`__plt_resolve_trampoline`]. The trampoline will then
1006// call this function to resolve the symbol and update the respective GOT entry.
1007// The trampoline will then jump to the resolved symbol.
1008//
1009// FIXME(andypython): 32-bit
1010#[cfg(target_pointer_width = "64")]
1011extern "C" fn __plt_resolve_inner(obj: *const DSO, relocation_index: c_uint) -> *mut c_void {
1012    let obj = unsafe { &*obj };
1013    let obj_base = obj.mmap.as_ptr() as usize;
1014    let jmprel = obj.dynamic.jmprel;
1015
1016    let rela = unsafe { &*(jmprel as *const Rela).add(relocation_index as usize) };
1017    assert_eq!(rela.r_type(NativeEndian, false), elf::R_X86_64_JUMP_SLOT);
1018
1019    let sym = obj
1020        .dynamic
1021        .symbol(rela.symbol(NativeEndian, false).unwrap())
1022        .expect("symbol not found");
1023    assert_ne!(sym.st_name(NativeEndian), 0);
1024
1025    let name = core::str::from_utf8(
1026        obj.dynamic
1027            .dynstrtab
1028            .get(sym.st_name(NativeEndian))
1029            .unwrap(),
1030    )
1031    .expect("non utf8 symbol name");
1032
1033    let resolved = resolve_sym(name, &[&GLOBAL_SCOPE.read(), obj.scope()])
1034        .map(|(sym, _, _)| sym)
1035        .unwrap_or_else(|| panic!("symbol '{name}' not found"))
1036        .as_ptr();
1037
1038    let ptr = if obj.pie {
1039        (obj_base as u64 + rela.r_offset(NativeEndian)) as *mut u64
1040    } else {
1041        rela.r_offset(NativeEndian) as *mut u64
1042    };
1043    #[cfg(feature = "trace_tls")]
1044    log::trace!("@plt: {} -> *mut {:p}", name, ptr);
1045
1046    unsafe { *ptr = resolved as u64 }
1047    resolved
1048}
1049
1050unsafe extern "C" {
1051    pub(super) fn __plt_resolve_trampoline() -> usize;
1052}
1053
1054#[cfg(target_arch = "x86_64")]
1055core::arch::global_asm!(
1056    "
1057.global __plt_resolve_trampoline
1058.hidden __plt_resolve_trampoline
1059__plt_resolve_trampoline:
1060    push    rsi
1061    push    rdi
1062 
1063    mov     rdi, qword ptr [rsp + 0x10]
1064    mov     rsi, qword ptr [rsp + 0x18]
1065
1066    // stash the floating point argument registers
1067    sub     rsp, 128
1068    movdqu  [rsp + 0x00], xmm0
1069    movdqu  [rsp + 0x10], xmm1
1070    movdqu  [rsp + 0x20], xmm2
1071    movdqu  [rsp + 0x30], xmm3
1072    movdqu  [rsp + 0x40], xmm4
1073    movdqu  [rsp + 0x50], xmm5
1074    movdqu  [rsp + 0x60], xmm6
1075    movdqu  [rsp + 0x70], xmm7
1076
1077    push   rax
1078    push   rcx
1079    push   rdx
1080    push   r8
1081    push   r9
1082    push   r10
1083
1084    push   rbp
1085    mov    rbp, rsp
1086    and    rsp, 0xfffffffffffffff0
1087    call   {__plt_resolve_inner}
1088    mov    r11, rax
1089    mov    rsp, rbp
1090    pop    rbp
1091
1092    pop    r10
1093    pop    r9
1094    pop    r8
1095    pop    rdx
1096    pop    rcx
1097    pop    rax
1098
1099    movdqu  xmm7, [rsp + 0x70]
1100    movdqu  xmm6, [rsp + 0x60]
1101    movdqu  xmm5, [rsp + 0x50]
1102    movdqu  xmm4, [rsp + 0x40]
1103    movdqu  xmm3, [rsp + 0x30]
1104    movdqu  xmm2, [rsp + 0x20]
1105    movdqu  xmm1, [rsp + 0x10]
1106    movdqu  xmm0, [rsp + 0x00]
1107    add     rsp, 128
1108
1109    pop    rdi
1110    pop    rsi
1111
1112    add    rsp, 0x10
1113    jmp    r11
1114
1115    ud2
1116.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1117",
1118    __plt_resolve_inner = sym __plt_resolve_inner
1119);
1120
1121#[cfg(target_arch = "x86")]
1122core::arch::global_asm!(
1123    "
1124.global __plt_resolve_trampoline
1125.hidden __plt_resolve_trampoline
1126__plt_resolve_trampoline:
1127    ud2
1128.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1129    "
1130);
1131
1132#[cfg(target_arch = "aarch64")]
1133core::arch::global_asm!(
1134    "
1135.global __plt_resolve_trampoline
1136.hidden __plt_resolve_trampoline
1137__plt_resolve_trampoline:
1138    udf #0
1139.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1140    "
1141);
1142
1143#[cfg(target_arch = "riscv64")]
1144core::arch::global_asm!(
1145    "
1146.global __plt_resolve_trampoline
1147.hidden __plt_resolve_trampoline
1148__plt_resolve_trampoline:
1149    unimp
1150.size __plt_resolve_trampoline, . - __plt_resolve_trampoline
1151    "
1152);