dynlink/context/
syms.rs

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