dynlink/context/
syms.rs

1use super::{Context, LoadedOrUnloaded};
2use crate::{
3    library::{Library, LibraryId},
4    symbol::{LookupFlags, RelocatedSymbol},
5    DynlinkError, DynlinkErrorKind,
6};
7
8impl Context {
9    /// Search for a symbol, starting from library denoted by start_id. For normal symbol lookup,
10    /// this should be the ID of the library that needs a symbol looked up. Flags can be
11    /// specified which allow control over where to look for the symbol.
12    pub fn lookup_symbol<'a>(
13        &'a self,
14        start_id: LibraryId,
15        name: &str,
16        lookup_flags: LookupFlags,
17    ) -> Result<RelocatedSymbol<'a>, DynlinkError> {
18        let allow_weak = lookup_flags.contains(LookupFlags::ALLOW_WEAK);
19        let start_lib = self.get_library(start_id)?;
20        // First try looking up within ourselves.
21        if !lookup_flags.contains(LookupFlags::SKIP_SELF) {
22            if let Ok(sym) = start_lib.lookup_symbol(name, allow_weak, false) {
23                return Ok(sym);
24            }
25        }
26
27        // Next, try all of our transitive dependencies.
28        if !lookup_flags.contains(LookupFlags::SKIP_DEPS) {
29            let mut visit = petgraph::visit::Bfs::new(&self.library_deps, start_id.0);
30            while let Some(node) = visit.next(&self.library_deps) {
31                let dep = &self.library_deps[node];
32                if node != start_id.0 {
33                    match dep {
34                        LoadedOrUnloaded::Unloaded(_) => {}
35                        LoadedOrUnloaded::Loaded(dep) => {
36                            if lookup_flags.contains(LookupFlags::SKIP_SECGATE_CHECK)
37                                || dep.is_local_or_secgate_from(start_lib, name)
38                            {
39                                let allow_weak =
40                                    allow_weak && dep.in_same_compartment_as(start_lib);
41                                let try_prefix =
42                                    dep.in_same_compartment_as(start_lib) || dep.allows_gates();
43                                if let Ok(sym) = dep.lookup_symbol(name, allow_weak, try_prefix) {
44                                    return Ok(sym);
45                                }
46                            }
47                        }
48                    }
49                }
50            }
51        }
52
53        // Fall back to global search.
54        if !lookup_flags.contains(LookupFlags::SKIP_GLOBAL) {
55            tracing::trace!("falling back to global search for {}", name);
56
57            let res = self.lookup_symbol_global(start_lib, name, lookup_flags);
58            if res.is_ok() {
59                return res;
60            }
61
62            if !allow_weak {
63                let res =
64                    self.lookup_symbol(start_id, name, lookup_flags.union(LookupFlags::ALLOW_WEAK));
65                if res.is_ok() {
66                    return res;
67                }
68            }
69        }
70        Err(DynlinkErrorKind::NameNotFound {
71            name: name.to_string(),
72        }
73        .into())
74    }
75
76    pub(crate) fn lookup_symbol_global<'a>(
77        &'a self,
78        start_lib: &Library,
79        name: &str,
80        lookup_flags: LookupFlags,
81    ) -> Result<RelocatedSymbol<'a>, DynlinkError> {
82        for idx in self.library_deps.node_indices() {
83            let dep = &self.library_deps[idx];
84            match dep {
85                LoadedOrUnloaded::Unloaded(_) => {}
86                LoadedOrUnloaded::Loaded(dep) => {
87                    if lookup_flags.contains(LookupFlags::SKIP_SECGATE_CHECK)
88                        || dep.is_local_or_secgate_from(start_lib, name)
89                    {
90                        let allow_weak = lookup_flags.contains(LookupFlags::ALLOW_WEAK)
91                            && dep.in_same_compartment_as(start_lib);
92                        let try_prefix = (idx != start_lib.id().0 || dep.allows_self_gates())
93                            && (dep.allows_gates() || dep.in_same_compartment_as(start_lib));
94                        if let Ok(sym) = dep.lookup_symbol(name, allow_weak, try_prefix) {
95                            return Ok(sym);
96                        }
97                    }
98                }
99            }
100        }
101        Err(DynlinkErrorKind::NameNotFound {
102            name: name.to_string(),
103        }
104        .into())
105    }
106}