1use crate::{
2 OpenFlags, Result,
3 loader::{Dylib, ElfLibrary, builtin, create_lazy_scope, deal_unknown},
4 register::{DylibState, MANAGER, register},
5};
6use alloc::{borrow::ToOwned, sync::Arc, vec::Vec};
7use core::ffi::{c_char, c_int, c_void};
8use elf_loader::RelocatedDylib;
9
10impl ElfLibrary {
11 #[cfg(feature = "std")]
22 #[inline]
23 pub fn dlopen(path: impl AsRef<std::ffi::OsStr>, flags: OpenFlags) -> Result<Dylib> {
24 dlopen_impl(path.as_ref().to_str().unwrap(), flags, || {
25 ElfLibrary::from_file(path.as_ref(), flags)
26 })
27 }
28
29 #[inline]
32 pub fn dlopen_from_binary(
33 bytes: &[u8],
34 path: impl AsRef<str>,
35 flags: OpenFlags,
36 ) -> Result<Dylib> {
37 dlopen_impl(path.as_ref(), flags, || {
38 ElfLibrary::from_binary(bytes, path.as_ref(), flags)
39 })
40 }
41}
42
43struct Recycler {
44 is_recycler: bool,
45 old_all_len: usize,
46 old_global_len: usize,
47}
48
49impl Drop for Recycler {
50 fn drop(&mut self) {
51 if self.is_recycler {
52 log::debug!("Destroying newly added dynamic libraries from the global");
53 let mut lock = MANAGER.write();
54 lock.all.truncate(self.old_all_len);
55 lock.global.truncate(self.old_global_len);
56 }
57 }
58}
59
60fn dlopen_impl(path: &str, flags: OpenFlags, f: impl Fn() -> Result<ElfLibrary>) -> Result<Dylib> {
61 let shortname = path.split('/').last().unwrap();
62 log::info!("dlopen: Try to open [{}] with [{:?}] ", path, flags);
63 let reader = MANAGER.read();
64 let mut new_libs = Vec::new();
66 let core = if flags.contains(OpenFlags::CUSTOM_NOT_REGISTER) {
67 let lib = f()?;
68 let core = lib.dylib.core_component().clone();
69 new_libs.push(Some(lib));
70 unsafe { RelocatedDylib::from_core_component(core) }
71 } else {
72 if let Some(lib) = reader.all.get(shortname) {
74 if lib.deps().is_some()
75 && !flags
76 .difference(lib.flags())
77 .contains(OpenFlags::RTLD_GLOBAL)
78 {
79 return Ok(lib.get_dylib());
80 }
81 lib.relocated_dylib()
82 } else {
83 let lib = f()?;
84 let core = lib.dylib.core_component().clone();
85 new_libs.push(Some(lib));
86 unsafe { RelocatedDylib::from_core_component(core) }
87 }
88 };
89
90 drop(reader);
91
92 let mut recycler = Recycler {
93 is_recycler: true,
94 old_all_len: usize::MAX,
95 old_global_len: usize::MAX,
96 };
97
98 let mut dep_libs = Vec::new();
100 let mut cur_pos = 0;
101 dep_libs.push(core.clone());
102 let mut lock = MANAGER.write();
103 recycler.old_all_len = lock.all.len();
104 recycler.old_global_len = lock.global.len();
105
106 register(core, flags, None, &mut lock, DylibState::default());
107
108 #[cfg(feature = "std")]
109 let mut cur_newlib_pos = 0;
110 while cur_pos < dep_libs.len() {
112 let lib_names: &[&str] = unsafe { core::mem::transmute(dep_libs[cur_pos].needed_libs()) };
113 #[cfg(feature = "std")]
114 let mut cur_rpath = None;
115 for lib_name in lib_names {
116 if let Some(lib) = lock.all.get_mut(*lib_name) {
117 if !lib.state.is_used() {
118 lib.state.set_used();
119 dep_libs.push(lib.relocated_dylib());
120 log::debug!("Use an existing dylib: [{}]", lib.shortname());
121 if flags
122 .difference(lib.flags())
123 .contains(OpenFlags::RTLD_GLOBAL)
124 {
125 let shortname = lib.shortname().to_owned();
126 log::debug!(
127 "Trying to update a library. Name: [{}] Old flags:[{:?}] New flags:[{:?}]",
128 shortname,
129 lib.flags(),
130 flags
131 );
132 lib.set_flags(flags);
133 let core = lib.relocated_dylib();
134 lock.global.insert(shortname, core);
135 }
136 }
137 continue;
138 }
139
140 #[cfg(feature = "std")]
141 {
142 let rpath = if let Some(rpath) = &cur_rpath {
143 rpath
144 } else {
145 let parent_lib = new_libs[cur_newlib_pos].as_ref().unwrap();
146 cur_rpath = Some(
147 parent_lib
148 .dylib
149 .rpath()
150 .map(|rpath| imp::fixup_rpath(parent_lib.name(), rpath))
151 .unwrap_or(Box::new([])),
152 );
153 cur_newlib_pos += 1;
154 unsafe { cur_rpath.as_ref().unwrap_unchecked() }
155 };
156
157 imp::find_library(rpath, lib_name, |file, file_path| {
158 let new_lib =
159 ElfLibrary::from_open_file(file, file_path.to_str().unwrap(), flags)?;
160 let inner = new_lib.dylib.core_component().clone();
161 register(
162 unsafe { RelocatedDylib::from_core_component(inner.clone()) },
163 flags,
164 None,
165 &mut lock,
166 *DylibState::default()
167 .set_used()
168 .set_new_idx(new_libs.len() as _),
169 );
170 dep_libs.push(unsafe { RelocatedDylib::from_core_component(inner) });
171 new_libs.push(Some(new_lib));
172 Ok(())
173 })?;
174 }
175
176 #[cfg(not(feature = "std"))]
177 return Err(crate::find_lib_error(alloc::format!(
178 "can not find file: {}",
179 lib_name
180 )));
181 }
182 cur_pos += 1;
183 }
184
185 #[derive(Clone, Copy)]
186 struct Item {
187 idx: usize,
188 next: usize,
189 }
190 let mut stack = Vec::new();
192 stack.push(Item { idx: 0, next: 0 });
193 let mut order = Vec::new();
195
196 'start: while let Some(mut item) = stack.pop() {
197 let names = new_libs[item.idx].as_ref().unwrap().needed_libs();
198 for name in names.iter().skip(item.next) {
199 let lib = lock.all.get_mut(*name).unwrap();
200 lib.state.set_unused();
201 let Some(idx) = lib.state.get_new_idx() else {
203 continue;
204 };
205 lib.state.set_relocated();
207 item.next += 1;
208 stack.push(item);
209 stack.push(Item {
210 idx: idx as usize,
211 next: 0,
212 });
213 continue 'start;
214 }
215 order.push(item.idx);
216 }
217
218 let read_lock = lock.downgrade();
219 for idx in order {
220 let lib = core::mem::take(&mut new_libs[idx]).unwrap();
221 let lazy_scope = create_lazy_scope(&dep_libs, lib.dylib.is_lazy());
222 log::debug!("Relocating dylib [{}]", lib.name());
223 let iter = read_lock.global.values().chain(dep_libs.iter());
224 lib.dylib.relocate(
225 iter,
226 &|name| builtin::BUILTIN.get(name).copied(),
227 deal_unknown,
228 lazy_scope,
229 )?;
230 }
231 drop(read_lock);
232 let deps = Arc::new(dep_libs.into_boxed_slice());
233 if !flags.contains(OpenFlags::CUSTOM_NOT_REGISTER) {
234 recycler.is_recycler = false;
235 }
236 let core = deps[0].clone();
237
238 let res = Dylib {
239 inner: core.clone(),
240 flags,
241 deps: Some(deps.clone()),
242 };
243 register(
245 core,
246 flags,
247 Some(deps),
248 &mut MANAGER.write(),
249 *DylibState::default().set_relocated(),
250 );
251 Ok(res)
252}
253
254#[cfg(feature = "std")]
255pub mod imp {
256 use crate::{Result, find_lib_error};
257 use core::str::FromStr;
258 use dynamic_loader_cache::{Cache as LdCache, Result as LdResult};
259 use spin::Lazy;
260 use std::path::PathBuf;
261
262 static LD_LIBRARY_PATH: Lazy<Box<[PathBuf]>> = Lazy::new(|| {
263 let library_path = std::env::var("LD_LIBRARY_PATH").unwrap_or(String::new());
264 deal_path(&library_path)
265 });
266 static DEFAULT_PATH: spin::Lazy<Box<[PathBuf]>> = Lazy::new(|| unsafe {
267 vec![
268 PathBuf::from_str("/lib").unwrap_unchecked(),
269 PathBuf::from_str("/usr/lib").unwrap_unchecked(),
270 ]
271 .into_boxed_slice()
272 });
273 static LD_CACHE: Lazy<Box<[PathBuf]>> = Lazy::new(|| {
274 build_ld_cache().unwrap_or_else(|err| {
275 log::warn!("Build ld cache failed: {}", err);
276 Box::new([])
277 })
278 });
279
280 #[inline]
281 fn build_ld_cache() -> LdResult<Box<[PathBuf]>> {
282 use std::collections::HashSet;
283
284 let cache = LdCache::load()?;
285 let unique_ld_foders = cache
286 .iter()?
287 .filter_map(LdResult::ok)
288 .map(|entry| {
289 entry.full_path.parent().unwrap().to_owned()
291 })
292 .collect::<HashSet<_>>();
293 Ok(Vec::from_iter(unique_ld_foders).into_boxed_slice())
294 }
295
296 #[inline]
297 pub(crate) fn fixup_rpath(lib_path: &str, rpath: &str) -> Box<[PathBuf]> {
298 if !rpath.contains('$') {
299 return deal_path(rpath);
300 }
301 for s in rpath.split('$').skip(1) {
302 if !s.starts_with("ORIGIN") && !s.starts_with("{ORIGIN}") {
303 log::warn!("DT_RUNPATH format is incorrect: [{}]", rpath);
304 return Box::new([]);
305 }
306 }
307 let dir = if let Some((path, _)) = lib_path.rsplit_once('/') {
308 path
309 } else {
310 "."
311 };
312 deal_path(&rpath.to_string().replace("$ORIGIN", dir))
313 }
314
315 #[inline]
316 fn deal_path(s: &str) -> Box<[PathBuf]> {
317 s.split(":")
318 .map(|str| std::path::PathBuf::try_from(str).unwrap())
319 .collect()
320 }
321
322 #[inline]
323 pub(crate) fn find_library(
324 cur_rpath: &Box<[PathBuf]>,
325 lib_name: &str,
326 mut f: impl FnMut(std::fs::File, &std::path::PathBuf) -> Result<()>,
327 ) -> Result<()> {
328 let search_paths = LD_LIBRARY_PATH
330 .iter()
331 .chain(cur_rpath.iter())
332 .chain(LD_CACHE.iter())
333 .chain(DEFAULT_PATH.iter());
334
335 for path in search_paths {
336 let file_path = path.join(lib_name);
337 log::trace!("Try to open dependency shared object: [{:?}]", file_path);
338 if let Ok(file) = std::fs::File::open(&file_path) {
339 match f(file, &file_path) {
340 Ok(_) => return Ok(()),
341 Err(err) => {
342 log::debug!("Cannot load dylib: [{:?}] reason: [{:?}]", file_path, err)
343 }
344 }
345 }
346 }
347 Err(find_lib_error(format!("can not find file: {}", lib_name)))
348 }
349}
350
351#[allow(unused_variables)]
352pub unsafe extern "C" fn dlopen(filename: *const c_char, flags: c_int) -> *const c_void {
354 let mut lib = if filename.is_null() {
355 MANAGER.read().all.get_index(0).unwrap().1.get_dylib()
356 } else {
357 #[cfg(feature = "std")]
358 {
359 let flags = OpenFlags::from_bits_retain(flags as _);
360 let filename = unsafe { core::ffi::CStr::from_ptr(filename) };
361 let path = filename.to_str().unwrap();
362 if let Ok(lib) = ElfLibrary::dlopen(path, flags) {
363 lib
364 } else {
365 return core::ptr::null();
366 }
367 }
368 #[cfg(not(feature = "std"))]
369 return core::ptr::null();
370 };
371 Arc::into_raw(core::mem::take(&mut lib.deps).unwrap()) as _
372}