1use crate::{
2 OpenFlags, Result,
3 core_impl::{
4 loader::{DylibExt, ElfDylib, ElfLibrary, LoadResult, LoadedDylib, create_lazy_scope},
5 register::{DylibState, GlobalDylib, MANAGER, Manager, register},
6 traits::AsFilename,
7 },
8 error::find_lib_error,
9 utils::ld_cache::LdCache,
10};
11use alloc::{
12 borrow::ToOwned,
13 boxed::Box,
14 format,
15 string::{String, ToString},
16 sync::Arc,
17 vec,
18 vec::Vec,
19};
20use core::ffi::{CStr, c_char, c_int, c_void};
21use spin::{Lazy, RwLockWriteGuard};
22
23#[derive(Debug, PartialEq, Eq, Hash)]
24pub(crate) struct ElfPath {
25 path: String,
26}
27
28impl ElfPath {
29 pub(crate) fn from_str(path: &str) -> Result<Self> {
30 Ok(ElfPath {
31 path: path.to_owned(),
32 })
33 }
34
35 fn join(&self, file_name: &str) -> ElfPath {
37 let mut new = self.path.clone();
38 if !new.is_empty() && !new.ends_with('/') {
39 new.push('/');
40 }
41 new.push_str(file_name);
42 ElfPath { path: new }
43 }
44
45 fn as_str(&self) -> &str {
46 &self.path
47 }
48}
49
50fn get_env(name: &str) -> Option<&'static str> {
51 unsafe {
52 let mut cur = crate::core_impl::types::ENVP;
53 if cur.is_null() {
54 return None;
55 }
56 while !(*cur).is_null() {
57 if let Ok(env) = CStr::from_ptr(*cur).to_str() {
58 if let Some((k, v)) = env.split_once('=') {
59 if k == name {
60 return Some(v);
61 }
62 }
63 }
64 cur = cur.add(1);
65 }
66 }
67 None
68}
69
70impl ElfLibrary {
71 pub fn this() -> ElfLibrary {
73 let reader = crate::lock_read!(MANAGER);
74 reader
75 .get_index(0)
76 .expect("Main executable must be initialized")
77 .1
78 .get_lib()
79 }
80
81 pub fn dlopen(path: impl AsFilename, flags: OpenFlags) -> Result<ElfLibrary> {
91 dlopen_impl(path.as_filename(), flags, None)
92 }
93
94 pub fn dlopen_from_binary(
97 bytes: &[u8],
98 path: impl AsFilename,
99 flags: OpenFlags,
100 ) -> Result<ElfLibrary> {
101 dlopen_impl(path.as_filename(), flags, Some(bytes))
102 }
103}
104
105struct OpenContext<'a> {
110 lock: Option<RwLockWriteGuard<'a, Manager>>,
113 new_libs: Vec<Option<ElfDylib>>,
115 added_names: Vec<String>,
117 dep_libs: Vec<LoadedDylib>,
119 flags: OpenFlags,
121 committed: bool,
123}
124
125impl<'a> Drop for OpenContext<'a> {
126 fn drop(&mut self) {
127 if !self.committed {
129 log::debug!("Destroying newly added dynamic libraries from the global");
130 let mut lock = self
131 .lock
132 .take()
133 .unwrap_or_else(|| crate::lock_write!(MANAGER));
134 for name in &self.added_names {
135 lock.remove(name);
136 }
137 }
138 }
139}
140
141impl<'a> OpenContext<'a> {
142 fn new(mut flags: OpenFlags) -> Self {
143 if get_env("LD_BIND_NOW").is_some() {
144 flags |= OpenFlags::RTLD_NOW;
145 }
146 let lock = crate::lock_write!(MANAGER);
147 Self {
148 lock: Some(lock),
149 new_libs: Vec::new(),
150 added_names: Vec::new(),
151 dep_libs: Vec::new(),
152 flags,
153 committed: false,
154 }
155 }
156
157 fn try_existing(&mut self, path: &str) -> Option<ElfLibrary> {
158 let shortname = path.rsplit_once('/').map_or(path, |(_, name)| name);
159 if let Some(lib) = self.wait_for_library(shortname) {
160 let elf_lib = lib.get_lib();
161 log::info!("dlopen: Found existing library [{}]", path);
162 self.lock
163 .as_mut()
164 .expect("Lock must be held")
165 .promote(shortname, self.flags);
166 self.committed = true;
167 return Some(elf_lib);
168 }
169 None
170 }
171
172 fn wait_for_library(&mut self, shortname: &str) -> Option<GlobalDylib> {
173 loop {
174 let entry = {
175 let lock = self.lock.as_ref().expect("Lock must be held");
176 lock.get(shortname).cloned()
177 };
178
179 match entry {
180 Some(lib) if lib.state.is_relocated() => return Some(lib),
181 Some(lib) if self.added_names.iter().any(|n| n == shortname) => {
182 return Some(lib);
185 }
186 Some(_) => {
187 drop(self.lock.take());
189 core::hint::spin_loop();
190 self.lock = Some(crate::lock_write!(MANAGER));
191 }
192 None => return None,
193 }
194 }
195 }
196
197 fn register_new(&mut self, lib: ElfDylib) -> LoadedDylib {
198 let core = lib.core();
199 let relocated = unsafe { LoadedDylib::from_core(core.clone()) };
200 let new_idx = self.new_libs.len();
201
202 let shortname = relocated.shortname().to_owned();
203 register(
204 relocated.clone(),
205 self.flags,
206 self.lock.as_mut().expect("Lock must be held"),
207 *DylibState::default().set_new_idx(new_idx as _),
208 );
209
210 self.dep_libs.push(relocated.clone());
211 self.added_names.push(shortname);
212 self.new_libs.push(Some(lib));
213
214 relocated
215 }
216
217 fn try_use_existing(&mut self, shortname: &str) -> bool {
218 if self.dep_libs.iter().any(|d| d.shortname() == shortname) {
221 return true;
222 }
223
224 if let Some(lib) = self.wait_for_library(shortname) {
225 self.dep_libs.push(lib.dylib());
228 log::debug!("Use an existing dylib: [{}]", lib.shortname());
229 self.lock
230 .as_mut()
231 .expect("Lock must be held")
232 .promote(shortname, self.flags);
233 return true;
234 }
235 false
236 }
237
238 fn load_and_register(
239 &mut self,
240 p: &ElfPath,
241 bytes: Option<&[u8]>,
242 ) -> Result<Option<Vec<String>>> {
243 match ElfLibrary::load(p.as_str(), bytes)? {
244 LoadResult::Dylib(lib) => {
245 self.register_new(lib);
246 Ok(None)
247 }
248 LoadResult::Script(libs) => Ok(Some(libs)),
249 }
250 }
251
252 fn load_deps(&mut self) -> Result<()> {
253 let mut cur_pos = 0;
254 while cur_pos < self.dep_libs.len() {
255 let lib_names = self.dep_libs[cur_pos].needed_libs().to_vec();
256 let mut cur_paths: Option<(Box<[ElfPath]>, Box<[ElfPath]>)> = None;
257
258 let parent_new_idx = {
260 let lock = self.lock.as_mut().expect("Lock must be held");
261 lock.get(self.dep_libs[cur_pos].shortname())
262 .expect("Library must be registered")
263 .state
264 .get_new_idx()
265 .map(|idx| idx as usize)
266 };
267
268 for lib_name in lib_names {
269 let (rpath, runpath): (&[ElfPath], &[ElfPath]) = if let Some((r, ru)) = &cur_paths {
270 (&**r, &**ru)
271 } else if let Some(idx) = parent_new_idx {
272 let parent_lib: &ElfDylib = self.new_libs[idx]
273 .as_ref()
274 .expect("New library must be available");
275 let new_rpath = parent_lib
276 .rpath()
277 .map(|r| fixup_rpath(parent_lib.name(), r))
278 .unwrap_or_default();
279 let new_runpath = parent_lib
280 .runpath()
281 .map(|r| fixup_rpath(parent_lib.name(), r))
282 .unwrap_or_default();
283 cur_paths = Some((new_rpath, new_runpath));
284 let (r, ru) = unsafe { cur_paths.as_ref().unwrap_unchecked() };
285 (&**r, &**ru)
286 } else {
287 (&[], &[])
288 };
289
290 self.find_library(rpath, runpath, &lib_name, None)?;
291 }
292 cur_pos += 1;
293 }
294 Ok(())
295 }
296
297 fn compute_order(&mut self) -> Vec<usize> {
298 if self.new_libs.is_empty() {
299 return Vec::new();
300 }
301
302 #[derive(Clone, Copy)]
303 struct Item {
304 idx: usize,
305 next: usize,
306 }
307 let mut stack = vec![Item { idx: 0, next: 0 }];
312 let mut order = Vec::new();
313
314 'start: while let Some(mut item) = stack.pop() {
315 let names = self.new_libs[item.idx]
316 .as_ref()
317 .expect("New library must be available")
318 .needed_libs();
319 for name in names.iter().skip(item.next) {
320 let lib = self
321 .lock
322 .as_mut()
323 .expect("Lock must be held")
324 .get_mut(*name)
325 .expect("Library must be registered");
326
327 if let Some(idx) = lib.state.get_new_idx() {
328 lib.state.set_relocated();
329 item.next += 1;
330 stack.push(item);
331 stack.push(Item {
332 idx: idx as usize,
333 next: 0,
334 });
335 continue 'start;
336 }
337 }
338 order.push(item.idx);
339 }
340 order
341 }
342
343 fn update_dependency_scopes(&mut self) {
344 log::debug!("Updating dependency scopes for new libraries");
345
346 let lock = self.lock.as_mut().expect("Lock must be held");
347 let new_lib_names = self
349 .new_libs
350 .iter()
351 .filter_map(|lib_opt| lib_opt.as_ref())
352 .map(|lib| lib.short_name());
353
354 crate::core_impl::register::update_dependency_scopes(lock, new_lib_names);
355 }
356
357 fn set_relocating(&mut self) {
359 let lock = self.lock.as_mut().expect("Lock must be held");
360 for lib in &self.dep_libs {
361 lock.get_mut(lib.shortname())
362 .expect("Library must be registered")
363 .state
364 .set_relocating();
365 }
366
367 drop(self.lock.take());
369 }
370
371 fn set_relocated(&self) {
374 let mut lock = crate::lock_write!(MANAGER);
375 for lib in &self.dep_libs {
376 lock.get_mut(lib.shortname())
377 .expect("Library must be registered")
378 .state
379 .set_relocated();
380 }
381 }
382
383 fn relocate(&mut self, order: &[usize], deps: &Arc<[LoadedDylib]>) -> Result<()> {
385 self.set_relocating();
387
388 let lazy_scope = create_lazy_scope(deps, self.flags);
389 let global_libs = {
390 let lock = crate::lock_read!(MANAGER);
391 lock.global_values().cloned().collect::<Vec<_>>()
392 };
393
394 for &idx in order {
395 let lib = core::mem::take(&mut self.new_libs[idx]).expect("Library missing");
396 log::debug!("Relocating dylib [{}]", lib.name());
397 let is_lazy = if self.flags.is_now() {
398 false
399 } else if self.flags.is_lazy() {
400 true
401 } else {
402 lib.is_lazy()
403 };
404
405 let scope = if self.flags.is_deepbind() {
406 deps.iter().chain(global_libs.iter())
407 } else {
408 global_libs.iter().chain(deps.iter())
409 };
410
411 lib.relocator()
412 .scope(scope.cloned())
413 .lazy(is_lazy)
414 .lazy_scope(lazy_scope.clone())
415 .relocate()?;
416 }
417
418 self.set_relocated();
420
421 Ok(())
422 }
423
424 fn get_deps(&self) -> Arc<[LoadedDylib]> {
426 let lock = self.lock.as_ref().expect("Lock must be held");
427 let shortname = self.dep_libs[0].shortname();
428 lock.get(shortname)
429 .expect("Root library must be registered")
430 .deps
431 .clone()
432 .expect("Dependency scope must be computed")
433 }
434
435 fn finish(mut self, deps: Arc<[LoadedDylib]>) -> ElfLibrary {
437 self.committed = true;
438 let core = deps[0].clone();
439 ElfLibrary {
440 inner: core,
441 deps: Some(deps),
442 }
443 }
444
445 fn load_root(&mut self, path: &str, bytes: Option<&[u8]>) -> Result<Option<ElfLibrary>> {
446 if let Some(lib) = self.try_existing(path) {
447 return Ok(Some(lib));
448 }
449
450 if self.flags.is_noload() {
451 return Err(find_lib_error(format!("can not find file: {}", path)));
452 }
453
454 self.find_library(&[], &[], path, bytes)?;
455 Ok(None)
456 }
457
458 fn find_library(
459 &mut self,
460 rpath: &[ElfPath],
461 runpath: &[ElfPath],
462 lib_name: &str,
463 bytes: Option<&[u8]>,
464 ) -> Result<()> {
465 let shortname = lib_name.rsplit_once('/').map_or(lib_name, |(_, name)| name);
466 if self.try_use_existing(shortname) {
467 return Ok(());
468 }
469
470 if lib_name.contains('/') {
472 if let Ok(path) = ElfPath::from_str(lib_name) {
473 return self.try_load_internal(rpath, runpath, &path, bytes);
474 }
475 }
476
477 let rpath_dirs = if runpath.is_empty() { rpath } else { &[] };
479 for dir in rpath_dirs
480 .iter()
481 .chain(LD_LIBRARY_PATH.iter())
482 .chain(runpath.iter())
483 {
484 if self
485 .try_load_internal(rpath, runpath, &dir.join(lib_name), bytes)
486 .is_ok()
487 {
488 return Ok(());
489 }
490 }
491
492 if let Some(cache) = &*LD_CACHE {
494 if let Some(path) = cache.lookup(lib_name) {
495 if let Ok(path) = ElfPath::from_str(&path) {
496 if self.try_load_internal(rpath, runpath, &path, bytes).is_ok() {
497 return Ok(());
498 }
499 }
500 }
501 }
502
503 for dir in DEFAULT_PATH.iter() {
505 if self
506 .try_load_internal(rpath, runpath, &dir.join(lib_name), bytes)
507 .is_ok()
508 {
509 return Ok(());
510 }
511 }
512
513 Err(find_lib_error(format!(
514 "can not find library: {}",
515 lib_name
516 )))
517 }
518
519 fn try_load_internal(
520 &mut self,
521 rpath: &[ElfPath],
522 runpath: &[ElfPath],
523 path: &ElfPath,
524 bytes: Option<&[u8]>,
525 ) -> Result<()> {
526 let Some(libs) = self.load_and_register(path, bytes)? else {
527 return Ok(());
528 };
529 for lib in libs {
530 self.find_library(rpath, runpath, &lib, None)?;
531 }
532 Ok(())
533 }
534}
535
536fn dlopen_impl(path: &str, flags: OpenFlags, bytes: Option<&[u8]>) -> Result<ElfLibrary> {
537 let mut ctx = OpenContext::new(flags);
538
539 log::info!("dlopen: Try to open [{}] with [{:?}] ", path, ctx.flags);
541
542 if let Some(lib) = ctx.load_root(path, bytes)? {
543 return Ok(lib);
544 }
545
546 ctx.load_deps()?;
548
549 ctx.update_dependency_scopes();
551
552 let order = ctx.compute_order();
554
555 let deps = ctx.get_deps();
557 ctx.relocate(&order, &deps)?;
558
559 Ok(ctx.finish(deps))
561}
562
563static LD_LIBRARY_PATH: Lazy<Box<[ElfPath]>> = Lazy::new(|| {
564 if let Some(path) = get_env("LD_LIBRARY_PATH") {
565 parse_path_list(path)
566 } else {
567 Box::new([])
568 }
569});
570static DEFAULT_PATH: Lazy<Box<[ElfPath]>> = Lazy::new(|| unsafe {
571 let v = vec![
572 ElfPath::from_str("/lib").unwrap_unchecked(),
573 ElfPath::from_str("/usr/lib").unwrap_unchecked(),
574 ElfPath::from_str("/lib64").unwrap_unchecked(),
575 ElfPath::from_str("/usr/lib64").unwrap_unchecked(),
576 ];
577 v.into_boxed_slice()
578});
579static LD_CACHE: Lazy<Option<LdCache>> = Lazy::new(|| LdCache::new().ok());
580
581#[inline]
582fn fixup_rpath(lib_path: &str, rpath: &str) -> Box<[ElfPath]> {
583 if !rpath.contains('$') {
584 return parse_path_list(rpath);
585 }
586 for s in rpath.split('$').skip(1) {
587 if !s.starts_with("ORIGIN") && !s.starts_with("{ORIGIN}") {
588 log::warn!("DT_RUNPATH format is incorrect: [{}]", rpath);
589 return Box::new([]);
590 }
591 }
592 let dir = if let Some((path, _)) = lib_path.rsplit_once('/') {
593 path
594 } else {
595 "."
596 };
597 parse_path_list(&rpath.to_string().replace("$ORIGIN", dir))
598}
599
600#[inline]
602fn parse_path_list(s: &str) -> Box<[ElfPath]> {
603 s.split(':')
604 .filter(|str| !str.is_empty())
605 .map(|str| ElfPath::from_str(str).unwrap())
606 .collect()
607}
608
609#[unsafe(no_mangle)]
612pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *const c_void {
613 let lib = if filename.is_null() {
614 ElfLibrary::this()
615 } else {
616 let flags = OpenFlags::from_bits_retain(flags as _);
617 let filename = unsafe { CStr::from_ptr(filename) };
618 let Ok(path) = filename.to_str() else {
619 return core::ptr::null();
620 };
621 if let Ok(lib) = ElfLibrary::dlopen(path, flags) {
622 lib
623 } else {
624 return core::ptr::null();
625 }
626 };
627 Box::into_raw(Box::new(lib)) as _
628}