1use crate::{Dylib, OpenFlags};
2use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc};
3use elf_loader::RelocatedDylib;
4use indexmap::IndexMap;
5use spin::{Lazy, RwLock};
6
7impl Drop for Dylib {
8 fn drop(&mut self) {
9 if self.flags.contains(OpenFlags::RTLD_NODELETE)
10 | self.flags.contains(OpenFlags::CUSTOM_NOT_REGISTER)
11 {
12 return;
13 }
14 let ref_count = self.inner.strong_count();
15 let threshold =
17 2 + self.deps.is_some() as usize + self.flags.contains(OpenFlags::RTLD_GLOBAL) as usize;
18 if ref_count == threshold {
19 log::info!("Destroying dylib [{}]", self.inner.shortname());
20 let mut lock = MANAGER.write();
21 lock.all.shift_remove(self.inner.shortname());
22 if self.flags.contains(OpenFlags::RTLD_GLOBAL) {
23 lock.global.shift_remove(self.inner.shortname());
24 }
25 for dep in self.deps.as_ref().unwrap().iter().skip(1) {
26 let dep_threshold = if let Some(lib) = lock.all.get(dep.shortname()) {
27 if lib.flags.contains(OpenFlags::RTLD_NODELETE) {
28 continue;
29 }
30 2 + lib.deps.is_some() as usize
31 + lib.flags.contains(OpenFlags::RTLD_GLOBAL) as usize
32 } else {
33 continue;
34 };
35 if dep.strong_count() == dep_threshold {
36 log::info!("Destroying dylib [{}]", dep.shortname());
37 lock.all.shift_remove(dep.shortname());
38 lock.global.shift_remove(self.inner.shortname());
39 }
40 }
41 }
42 }
43}
44
45#[derive(Clone, Copy)]
46pub(crate) struct DylibState(u8);
47
48impl Default for DylibState {
49 fn default() -> Self {
50 Self(0)
51 }
52}
53
54impl DylibState {
55 const USED_MASK: u8 = 0b10000000;
56 const RELOCATED: u8 = 0b01111111;
57
58 #[inline]
59 pub(crate) fn set_unused(&mut self) -> &mut Self {
60 self.0 &= !Self::USED_MASK;
61 self
62 }
63
64 #[inline]
65 pub(crate) fn set_used(&mut self) -> &mut Self {
66 self.0 |= Self::USED_MASK;
67 self
68 }
69
70 #[inline]
71 pub(crate) fn is_used(&self) -> bool {
72 self.0 & Self::USED_MASK != 0
73 }
74
75 #[inline]
76 pub(crate) fn get_new_idx(&self) -> Option<u8> {
77 let remove_used_bit = self.0 & !Self::USED_MASK;
78 if remove_used_bit == Self::RELOCATED {
79 None
80 } else {
81 Some(remove_used_bit)
82 }
83 }
84
85 #[inline]
86 pub(crate) fn set_relocated(&mut self) -> &mut Self {
87 self.0 |= Self::RELOCATED;
88 self
89 }
90
91 #[allow(unused)]
92 #[inline]
93 pub(crate) fn set_new_idx(&mut self, idx: u8) -> &mut Self {
94 assert!(idx < Self::RELOCATED);
95 self.0 |= idx;
96 self
97 }
98}
99
100#[derive(Clone)]
101pub(crate) struct GlobalDylib {
102 inner: RelocatedDylib<'static>,
103 flags: OpenFlags,
104 deps: Option<Arc<Box<[RelocatedDylib<'static>]>>>,
105 pub(crate) state: DylibState,
106}
107
108unsafe impl Send for GlobalDylib {}
109unsafe impl Sync for GlobalDylib {}
110
111impl GlobalDylib {
112 #[inline]
113 pub(crate) fn get_dylib(&self) -> Dylib {
114 debug_assert!(self.deps.is_some());
115 Dylib {
116 inner: self.inner.clone(),
117 flags: self.flags,
118 deps: self.deps.clone(),
119 }
120 }
121
122 #[inline]
123 pub(crate) fn set_flags(&mut self, flags: OpenFlags) {
124 self.flags = flags;
125 }
126
127 #[inline]
128 pub(crate) fn flags(&self) -> OpenFlags {
129 self.flags
130 }
131
132 #[inline]
133 pub(crate) fn relocated_dylib(&self) -> RelocatedDylib<'static> {
134 self.inner.clone()
135 }
136
137 #[inline]
138 pub(crate) fn relocated_dylib_ref(&self) -> &RelocatedDylib<'static> {
139 &self.inner
140 }
141
142 #[inline]
143 pub(crate) fn shortname(&self) -> &str {
144 self.inner.shortname()
145 }
146
147 #[inline]
148 pub(crate) fn deps(&self) -> Option<&Arc<Box<[RelocatedDylib<'static>]>>> {
149 self.deps.as_ref()
150 }
151}
152
153pub(crate) struct Manager {
154 pub(crate) all: IndexMap<String, GlobalDylib>,
155 pub(crate) global: IndexMap<String, RelocatedDylib<'static>>,
156}
157
158pub(crate) static MANAGER: Lazy<RwLock<Manager>> = Lazy::new(|| {
159 RwLock::new(Manager {
160 all: IndexMap::new(),
161 global: IndexMap::new(),
162 })
163});
164
165pub(crate) fn register(
166 lib: RelocatedDylib<'static>,
167 flags: OpenFlags,
168 deps: Option<Arc<Box<[RelocatedDylib<'static>]>>>,
169 manager: &mut Manager,
170 state: DylibState,
171) {
172 let shortname = lib.shortname().to_owned();
173 log::debug!(
174 "Trying to register a library. Name: [{}] flags:[{:?}]",
175 shortname,
176 flags
177 );
178 manager.all.insert(
179 shortname.to_owned(),
180 GlobalDylib {
181 state,
182 inner: lib.clone(),
183 flags,
184 deps,
185 },
186 );
187 if flags.contains(OpenFlags::RTLD_GLOBAL) {
188 manager.global.insert(shortname.to_owned(), lib);
189 }
190}
191
192#[cfg(feature = "std")]
193pub(crate) fn global_find(name: &str) -> Option<*const ()> {
194 log::debug!("Lazy Binding: [{}]", name);
195 crate::loader::builtin::BUILTIN
196 .get(name)
197 .copied()
198 .or_else(|| {
199 MANAGER.read().global.values().find_map(|lib| unsafe {
200 lib.get::<()>(name).map(|sym| {
201 log::trace!(
202 "Lazy Binding: find symbol [{}] from [{}] in global scope ",
203 name,
204 lib.name()
205 );
206 let val = sym.into_raw();
207 assert!(lib.base() != val as usize);
208 val
210 })
211 })
212 })
213}