1use crate::{
2 ElfLibrary, OpenFlags,
3 core_impl::loader::{DylibExt, LoadedDylib},
4};
5use alloc::{
6 borrow::ToOwned,
7 collections::{btree_set::BTreeSet, vec_deque::VecDeque},
8 string::String,
9 sync::Arc,
10 vec::Vec,
11};
12use core::ffi::{c_int, c_void};
13use indexmap::IndexMap;
14use spin::{Lazy, RwLock};
15
16#[macro_export]
17macro_rules! lock_write {
18 ($lock:expr) => {{ $lock.write() }};
19}
20
21#[macro_export]
22macro_rules! lock_read {
23 ($lock:expr) => {{ $lock.read() }};
24}
25
26impl Drop for ElfLibrary {
27 fn drop(&mut self) {
28 let mut removed_libs = Vec::new();
29 {
30 let mut lock = lock_write!(MANAGER);
31 let shortname = self.inner.shortname();
32 let Some(entry) = lock.get(shortname) else {
33 return;
34 };
35
36 if entry.flags.is_nodelete() {
37 return;
38 }
39
40 let ref_count = unsafe { self.inner.core_ref().strong_count() };
41 let has_global = entry.flags.is_global();
42 debug_assert!(self.deps.is_some());
44 let threshold = 3 + has_global as usize;
45
46 log::debug!(
47 "Drop ElfLibrary [{}], ref count: {}, threshold: {}",
48 self.inner.name(),
49 ref_count,
50 threshold
51 );
52
53 if ref_count == threshold {
54 log::info!("Destroying dylib [{}]", self.inner.name());
55 removed_libs.push(self.inner.clone());
56
57 lock.remove(shortname);
58
59 if let Some(deps) = self.deps.as_ref() {
61 for dep in deps.iter().skip(1) {
62 let dep_shortname = dep.shortname();
63 let Some(dep_lib) = lock.get(dep_shortname) else {
64 continue;
65 };
66 if dep_lib.flags.is_nodelete() {
67 continue;
68 }
69 debug_assert!(
70 dep_lib.deps.is_some(),
71 "Dependency [{}] must have its deps set",
72 dep.name()
73 );
74 let dep_threshold = 3 + dep_lib.flags.is_global() as usize;
76
77 if unsafe { dep.core_ref().strong_count() } == dep_threshold {
78 log::info!("Destroying dylib [{}]", dep.name());
79 removed_libs.push(dep.clone());
80 lock.remove(dep_shortname);
81 }
82 }
83 }
84 }
85 }
86 for lib in removed_libs {
87 let base = lib.base();
88 let range = base..(base + lib.mapped_len());
89 finalize(base as *mut _, Some(range));
90 }
91 }
92}
93
94#[derive(Clone, Copy, Default)]
101pub(crate) struct DylibState(u8);
102
103impl DylibState {
104 const RELOCATED: u8 = 0b11111111;
105 const RELOCATING: u8 = 0b11111110;
106
107 #[inline]
109 pub(crate) fn is_relocated(&self) -> bool {
110 self.0 == Self::RELOCATED
111 }
112
113 #[inline]
115 pub(crate) fn get_new_idx(&self) -> Option<u8> {
116 if self.0 >= Self::RELOCATING {
117 None
118 } else {
119 Some(self.0)
120 }
121 }
122
123 #[inline]
125 pub(crate) fn set_relocated(&mut self) -> &mut Self {
126 self.0 = Self::RELOCATED;
127 self
128 }
129
130 #[inline]
132 pub(crate) fn set_relocating(&mut self) -> &mut Self {
133 self.0 = Self::RELOCATING;
134 self
135 }
136
137 #[allow(unused)]
139 #[inline]
140 pub(crate) fn set_new_idx(&mut self, idx: u8) -> &mut Self {
141 assert!(idx < Self::RELOCATING);
142 self.0 = idx;
143 self
144 }
145}
146
147#[derive(Clone)]
152pub(crate) struct GlobalDylib {
153 inner: LoadedDylib,
154 pub(crate) flags: OpenFlags,
155 pub(crate) deps: Option<Arc<[LoadedDylib]>>,
158 pub(crate) state: DylibState,
160}
161
162unsafe impl Send for GlobalDylib {}
163unsafe impl Sync for GlobalDylib {}
164
165impl GlobalDylib {
166 #[inline]
167 pub(crate) fn get_lib(&self) -> ElfLibrary {
168 debug_assert!(self.deps.is_some());
169 ElfLibrary {
170 inner: self.inner.clone(),
171 deps: self.deps.clone(),
172 }
173 }
174
175 #[inline]
176 pub(crate) fn dylib(&self) -> LoadedDylib {
177 self.inner.clone()
178 }
179
180 #[inline]
181 pub(crate) fn dylib_ref(&self) -> &LoadedDylib {
182 &self.inner
183 }
184
185 #[inline]
186 pub(crate) fn shortname(&self) -> &str {
187 self.inner.shortname()
188 }
189
190 #[inline]
191 pub(crate) fn set_deps(&mut self, deps: Arc<[LoadedDylib]>) {
192 self.deps = Some(deps);
193 }
194}
195
196pub(crate) struct Manager {
198 all: IndexMap<String, GlobalDylib>,
201 global: IndexMap<String, LoadedDylib>,
203 adds: u64,
205 subs: u64,
207}
208
209impl Manager {
210 pub(crate) fn add_global(&mut self, name: String, lib: LoadedDylib) {
211 debug_assert!(
212 !self.global.contains_key(&name),
213 "Library [{}] is already in global scope",
214 name
215 );
216 log::trace!("Adding [{}] to global scope", name);
217 self.global.insert(name, lib);
218 }
219
220 pub(crate) fn add(&mut self, name: String, lib: GlobalDylib) {
221 let res = self.all.insert(name.clone(), lib);
222 debug_assert!(res.is_none(), "Library [{}] is already registered", name);
223 self.adds += 1;
224 log::trace!("Registered [{}] in all map", name);
225 }
226
227 pub(crate) fn remove(&mut self, shortname: &str) {
228 let lib = self
229 .all
230 .shift_remove(shortname)
231 .expect("Library is not registered");
232 self.subs += 1;
233 let res = self.global.shift_remove(shortname);
234 debug_assert!(
235 lib.flags.is_global() == res.is_some(),
236 "Inconsistent global scope state when removing [{}]",
237 shortname
238 );
239 }
240
241 #[inline]
242 pub(crate) fn get(&self, name: &str) -> Option<&GlobalDylib> {
243 self.all.get(name)
244 }
245
246 #[inline]
247 pub(crate) fn get_mut(&mut self, name: &str) -> Option<&mut GlobalDylib> {
248 self.all.get_mut(name)
249 }
250
251 #[inline]
252 pub(crate) fn contains(&self, name: &str) -> bool {
253 self.all.contains_key(name)
254 }
255
256 #[inline]
257 pub(crate) fn all_values(&self) -> indexmap::map::Values<'_, String, GlobalDylib> {
258 self.all.values()
259 }
260
261 #[inline]
262 pub(crate) fn global_values(&self) -> indexmap::map::Values<'_, String, LoadedDylib> {
263 self.global.values()
264 }
265
266 #[inline]
267 pub(crate) fn all_iter(&self) -> indexmap::map::Iter<'_, String, GlobalDylib> {
268 self.all.iter()
269 }
270
271 pub(crate) fn adds(&self) -> u64 {
272 self.adds
273 }
274
275 pub(crate) fn subs(&self) -> u64 {
276 self.subs
277 }
278
279 #[inline]
280 pub(crate) fn keys(&self) -> indexmap::map::Keys<'_, String, GlobalDylib> {
281 self.all.keys()
282 }
283
284 #[inline]
285 pub(crate) fn get_index(&self, index: usize) -> Option<(&String, &GlobalDylib)> {
286 self.all.get_index(index)
287 }
288
289 pub(crate) fn promote(&mut self, shortname: &str, flags: OpenFlags) {
290 let promotable = flags.promotable();
291 let entry = self.get_mut(shortname).expect("Library must be registered");
292 if !entry.flags.contains(promotable) {
293 entry.flags |= promotable;
294 if flags.is_global() {
295 let core = entry.inner.clone();
296 self.add_global(shortname.to_owned(), core);
297 }
298 }
299 }
300}
301
302pub(crate) static MANAGER: Lazy<RwLock<Manager>> = Lazy::new(|| {
304 RwLock::new(Manager {
305 all: IndexMap::new(),
306 global: IndexMap::new(),
307 adds: 0,
308 subs: 0,
309 })
310});
311
312pub(crate) fn register(
316 lib: LoadedDylib,
317 flags: OpenFlags,
318 manager: &mut Manager,
319 state: DylibState,
320) {
321 let name = lib.name();
322 let is_main = name.is_empty();
323 let shortname = lib.shortname().to_owned();
324
325 let mut flags = flags;
326 if name.contains("libc")
327 || name.contains("libpthread")
328 || name.contains("libdl")
329 || name.contains("libgcc_s")
330 || name.contains("ld-linux")
331 || name.contains("ld-musl")
332 {
333 flags |= OpenFlags::RTLD_NODELETE;
334 }
335
336 log::debug!(
337 "Registering library: [{}] (full path: [{}]) flags: [{:?}]",
338 shortname,
339 name,
340 flags
341 );
342
343 manager.add(
344 shortname.clone(),
345 GlobalDylib {
346 state,
347 inner: lib.clone(),
348 flags,
349 deps: None,
350 },
351 );
352 if flags.is_global() || is_main {
353 manager.add_global(shortname, lib);
354 }
355}
356
357pub(crate) unsafe fn global_find<'a, T>(name: &str) -> Option<crate::Symbol<'a, T>> {
361 lock_read!(MANAGER).global_values().find_map(|lib| unsafe {
362 lib.get::<T>(name).map(|sym| {
363 log::trace!(
364 "Lazy Binding: find symbol [{}] from [{}] in global scope ",
365 name,
366 lib.name()
367 );
368 core::mem::transmute(sym)
369 })
370 })
371}
372
373pub(crate) unsafe fn next_find<'a, T>(addr: usize, name: &str) -> Option<crate::Symbol<'a, T>> {
375 let lock = lock_read!(MANAGER);
376 let (idx, _) = lock.all_iter().enumerate().find(|(_, (_, v))| {
378 let start = v.inner.base();
379 let end = start + v.inner.mapped_len();
380 (start..end).contains(&addr)
381 })?;
382
383 lock.all_values().skip(idx + 1).find_map(|lib| unsafe {
385 lib.inner.get::<T>(name).map(|sym| {
386 log::trace!(
387 "dlsym: find symbol [{}] from [{}] via RTLD_NEXT",
388 name,
389 lib.inner.name()
390 );
391 core::mem::transmute(sym)
392 })
393 })
394}
395
396pub(crate) fn addr2dso(addr: usize) -> Option<ElfLibrary> {
397 log::trace!("addr2dso: addr [{:#x}]", addr);
398 crate::lock_read!(MANAGER).all_values().find_map(|v| {
401 let start = v.dylib_ref().base();
402 let end = start + v.dylib_ref().mapped_len();
403 if (start..end).contains(&addr) {
404 Some(v.get_lib())
405 } else {
406 None
407 }
408 })
409}
410
411pub(crate) fn update_dependency_scopes<'a>(
417 manager: &mut Manager,
418 roots: impl Iterator<Item = &'a str>,
419) {
420 for root_name in roots {
421 let Some(root_lib) = manager.get(root_name) else {
422 continue;
423 };
424
425 if root_lib.deps.is_some() {
426 continue;
427 }
428
429 let mut scope = Vec::new();
430 let mut visited = BTreeSet::new();
431 let mut queue = VecDeque::new();
432
433 visited.insert(root_name.to_owned());
434 queue.push_back(root_name.to_owned());
435
436 while let Some(current_name) = queue.pop_front() {
437 let Some(lib_entry) = manager.get(¤t_name) else {
438 continue;
439 };
440 let dylib = lib_entry.dylib();
441 scope.push(dylib.clone());
442
443 for needed in dylib.needed_libs() {
444 if !manager.contains(needed) {
445 continue;
446 }
447 if !visited.contains(needed) {
448 visited.insert(needed.to_owned());
449 queue.push_back(needed.to_owned());
450 }
451 }
452 }
453 if let Some(entry) = manager.get_mut(root_name) {
454 entry.set_deps(Arc::from(scope));
455 }
456 }
457}
458
459pub(crate) fn register_atexit(
460 dso_handle: *mut c_void,
461 func: unsafe extern "C" fn(*mut c_void),
462 arg: *mut c_void,
463) -> c_int {
464 DESTRUCTORS.write().push(Destructor {
465 dso_handle,
466 func,
467 arg,
468 });
469 0
470}
471
472struct Destructor {
473 dso_handle: *mut c_void,
474 func: unsafe extern "C" fn(*mut c_void),
475 arg: *mut c_void,
476}
477
478unsafe impl Send for Destructor {}
479unsafe impl Sync for Destructor {}
480
481static DESTRUCTORS: Lazy<RwLock<Vec<Destructor>>> = Lazy::new(|| RwLock::new(Vec::new()));
482
483pub(crate) fn finalize(dso_handle: *mut c_void, range: Option<core::ops::Range<usize>>) {
484 let mut to_run = Vec::new();
485 {
486 let mut range = range;
487 if range.is_none() && !dso_handle.is_null() {
488 let manager = MANAGER.read();
489 for v in manager.all_values() {
490 let start = v.dylib_ref().base();
491 let end = start + v.dylib_ref().mapped_len();
492 if (start..end).contains(&(dso_handle as usize)) {
493 range = Some(start..end);
494 break;
495 }
496 }
497 }
498
499 let mut all_destructors = DESTRUCTORS.write();
500 let mut i = 0;
501 while i < all_destructors.len() {
502 let matches = match (dso_handle.is_null(), &range) {
503 (true, _) => true, (false, Some(r)) => r.contains(&(all_destructors[i].dso_handle as usize)),
505 (false, None) => all_destructors[i].dso_handle == dso_handle,
506 };
507
508 if matches {
509 to_run.push(all_destructors.remove(i));
510 } else {
511 i += 1;
512 }
513 }
514 }
515 if !to_run.is_empty() {
516 log::debug!(
517 "Running {} destructors for handle {:p}",
518 to_run.len(),
519 dso_handle
520 );
521 }
522 for destructor in to_run.into_iter().rev() {
523 unsafe { (destructor.func)(destructor.arg) };
524 }
525}
526
527#[unsafe(no_mangle)]
528pub unsafe extern "C" fn __cxa_thread_atexit_impl(
529 func: unsafe extern "C" fn(*mut c_void),
530 arg: *mut c_void,
531 dso_handle: *mut c_void,
532) -> c_int {
533 register_atexit(dso_handle, func, arg)
534}
535
536#[unsafe(no_mangle)]
537pub unsafe extern "C" fn __cxa_atexit(
538 func: unsafe extern "C" fn(*mut c_void),
539 arg: *mut c_void,
540 dso_handle: *mut c_void,
541) -> c_int {
542 register_atexit(dso_handle, func, arg)
543}
544
545#[unsafe(no_mangle)]
546pub unsafe extern "C" fn __cxa_finalize(dso_handle: *mut c_void) {
547 finalize(dso_handle, None);
548}