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 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 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 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 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}