dynlink/context/
syms.rs

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