dlopen_rs/loader/
mod.rs

1pub(crate) mod builtin;
2pub(crate) mod ehframe;
3pub(crate) mod tls;
4
5#[cfg(feature = "debug")]
6use super::debug::DebugInfo;
7use crate::{
8    find_lib_error, find_symbol_error,
9    register::{register, DylibState, MANAGER},
10    OpenFlags, Result,
11};
12use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
13use core::{ffi::CStr, fmt::Debug};
14use ehframe::EhFrame;
15use elf_loader::{
16    abi::PT_GNU_EH_FRAME,
17    arch::{ElfRela, Phdr},
18    mmap::MmapImpl,
19    object::{ElfBinary, ElfObject},
20    segment::ElfSegments,
21    CoreComponentRef, ElfDylib, Loader, RelocatedDylib, Symbol, UserData,
22};
23
24pub(crate) const EH_FRAME_ID: u8 = 0;
25#[cfg(feature = "debug")]
26pub(crate) const DEBUG_INFO_ID: u8 = 1;
27#[cfg(feature = "tls")]
28const TLS_ID: u8 = 2;
29const CLOSURE: u8 = 3;
30
31#[inline]
32pub(crate) fn find_symbol<'lib, T>(
33    libs: &'lib [RelocatedDylib<'static>],
34    name: &str,
35) -> Result<Symbol<'lib, T>> {
36    log::info!("Get the symbol [{}] in [{}]", name, libs[0].shortname());
37    libs.iter()
38        .find_map(|lib| unsafe { lib.get::<T>(name) })
39        .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
40}
41
42/// An unrelocated dynamic library
43pub struct ElfLibrary {
44    pub(crate) dylib: ElfDylib,
45    pub(crate) flags: OpenFlags,
46}
47
48impl Debug for ElfLibrary {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        self.dylib.fmt(f)
51    }
52}
53
54#[inline(always)]
55#[allow(unused)]
56fn parse_phdr(
57    cname: &CStr,
58    phdr: &Phdr,
59    segments: &ElfSegments,
60    data: &mut UserData,
61) -> elf_loader::Result<()> {
62    match phdr.p_type {
63        PT_GNU_EH_FRAME => {
64            data.insert(
65                EH_FRAME_ID,
66                Box::new(EhFrame::new(
67                    phdr,
68                    segments.base()..segments.base() + segments.len(),
69                )),
70            );
71        }
72        #[cfg(feature = "debug")]
73        elf_loader::abi::PT_DYNAMIC => {
74            data.insert(
75                DEBUG_INFO_ID,
76                Box::new(unsafe {
77                    DebugInfo::new(
78                        segments.base(),
79                        cname.as_ptr() as _,
80                        segments.base() + phdr.p_vaddr as usize,
81                    )
82                }),
83            );
84        }
85        #[cfg(feature = "tls")]
86        elf_loader::abi::PT_TLS => {
87            data.insert(TLS_ID, Box::new(tls::ElfTls::new(phdr, segments.base())));
88        }
89        _ => {}
90    }
91    Ok(())
92}
93
94#[inline(always)]
95#[allow(unused)]
96pub(crate) fn deal_unknown<'scope>(
97    rela: &ElfRela,
98    lib: &ElfDylib,
99    mut deps: impl Iterator<Item = &'scope RelocatedDylib<'static>> + Clone,
100) -> bool {
101    #[cfg(feature = "tls")]
102    match rela.r_type() as _ {
103        elf_loader::arch::REL_DTPMOD => {
104            let r_sym = rela.r_symbol();
105            let r_off = rela.r_offset();
106            let ptr = (lib.base() + r_off) as *mut usize;
107            let cast = |core: &elf_loader::CoreComponent| unsafe {
108                core.user_data()
109                    .get(TLS_ID)
110                    .unwrap()
111                    .downcast_ref::<tls::ElfTls>()
112                    .unwrap_unchecked()
113                    .module_id()
114            };
115            if r_sym != 0 {
116                let (dynsym, syminfo) = lib.symtab().symbol_idx(r_sym);
117                if dynsym.is_local() {
118                    unsafe { ptr.write(cast(lib.core_component_ref())) };
119                    return true;
120                } else {
121                    if let Some(id) = deps.find_map(|lib| unsafe {
122                        lib.symtab()
123                            .lookup_filter(&syminfo)
124                            .map(|_| cast(lib.core_component_ref()))
125                    }) {
126                        unsafe { ptr.write(id) };
127                        return true;
128                    };
129                };
130            } else {
131                unsafe { ptr.write(cast(lib.core_component_ref())) };
132                return true;
133            }
134        }
135        _ => {}
136    }
137    log::error!("Relocating dylib [{}] failed!", lib.name());
138    false
139}
140
141#[inline]
142pub(crate) fn create_lazy_scope(
143    deps: &[RelocatedDylib],
144    is_lazy: bool,
145) -> Option<Box<dyn for<'a> Fn(&'a str) -> Option<*const ()>>> {
146    if is_lazy {
147        let deps_weak: Vec<CoreComponentRef> = deps
148            .iter()
149            .map(|dep| unsafe { dep.core_component_ref().downgrade() })
150            .collect();
151        Some(Box::new(move |name: &str| {
152            deps_weak.iter().find_map(|dep| unsafe {
153                RelocatedDylib::from_core_component(dep.upgrade().unwrap())
154                    .get::<()>(name)
155                    .map(|sym| sym.into_raw())
156            })
157        })
158            as Box<dyn Fn(&str) -> Option<*const ()> + 'static>)
159    } else {
160        None
161    }
162}
163
164fn from_impl(object: impl ElfObject, flags: OpenFlags) -> Result<ElfLibrary> {
165    #[allow(unused_mut)]
166    let mut loader = Loader::<MmapImpl>::new();
167    #[cfg(feature = "std")]
168    unsafe {
169        loader.set_init_params(
170            crate::init::ARGC,
171            (*core::ptr::addr_of!(crate::init::ARGV)).as_ptr() as usize,
172            crate::init::ENVP,
173        )
174    };
175    let lazy_bind = if flags.contains(OpenFlags::RTLD_LAZY) {
176        Some(true)
177    } else if flags.contains(OpenFlags::RTLD_NOW) {
178        Some(false)
179    } else {
180        None
181    };
182    let dylib = loader.load_dylib(object, lazy_bind, parse_phdr)?;
183    log::debug!(
184        "Loading dylib [{}] at address [0x{:x}-0x{:x}]",
185        dylib.name(),
186        dylib.base(),
187        dylib.base() + dylib.map_len()
188    );
189    let lib = ElfLibrary { dylib, flags };
190    Ok(lib)
191}
192
193impl ElfLibrary {
194    /// Find and load a elf dynamic library from path.
195    ///
196    /// The `path` argument may be either:
197    /// * The absolute path to the library;
198    /// * A relative (to the current working directory) path to the library.   
199    ///
200    /// The `flags` argument can control how dynamic libraries are loaded.
201    ///
202    /// # Examples
203    /// ```no_run
204    /// # use ::dlopen_rs::ElfLibrary;
205    /// let lib = ElfLibrary::from_file("/path/to/awesome.module", OpenFlags::RTLD_LOCAL)
206    ///		.unwrap();
207    /// ```
208    ///
209    #[cfg(feature = "std")]
210    #[inline]
211    pub fn from_file(path: impl AsRef<std::ffi::OsStr>, flags: OpenFlags) -> Result<Self> {
212        let path = path.as_ref().to_str().unwrap();
213        let file = std::fs::File::open(path)?;
214        Self::from_open_file(file, path, flags)
215    }
216
217    /// Creates a new `ElfLibrary` instance from an open file handle.
218    /// The `flags` argument can control how dynamic libraries are loaded.
219    /// # Examples
220    /// ```
221    /// let file = File::open("/path/to/awesome.module").unwrap();
222    /// let lib = ElfLibrary::from_open_file(file, "/path/to/awesome.module", OpenFlags::RTLD_LOCAL).unwrap();
223    /// ```
224    #[cfg(feature = "std")]
225    #[inline]
226    pub fn from_open_file(
227        file: std::fs::File,
228        path: impl AsRef<str>,
229        flags: OpenFlags,
230    ) -> Result<ElfLibrary> {
231        use std::os::fd::IntoRawFd;
232
233        use elf_loader::object;
234        let file = unsafe { object::ElfFile::from_owned_fd(path.as_ref(), file.into_raw_fd()) };
235        from_impl(file, flags)
236    }
237
238    /// Load a elf dynamic library from bytes.
239    /// The `flags` argument can control how dynamic libraries are loaded.
240    /// # Examples
241    ///
242    /// ```no_run
243    /// # use ::dlopen_rs::ElfLibrary;
244    /// let path = Path::new("/path/to/awesome.module");
245    /// let bytes = std::fs::read(path).unwrap();
246    /// let lib = ElfLibrary::from_binary(&bytes, "/path/to/awesome.module", OpenFlags::RTLD_LOCAL).unwarp();
247    /// ```
248    #[inline]
249    pub fn from_binary(
250        bytes: impl AsRef<[u8]>,
251        path: impl AsRef<str>,
252        flags: OpenFlags,
253    ) -> Result<Self> {
254        let file = ElfBinary::new(path.as_ref(), bytes.as_ref());
255        from_impl(file, flags)
256    }
257
258    /// Load an existing dynamic library using the shortname of the library
259    /// # Examples
260    /// ```no_run
261    /// # use ::dlopen_rs::ElfLibrary;
262    /// let libc = ElfLibrary::load_existing("libc.so.6").unwrap();
263    /// ```
264    pub fn load_existing(shortname: &str) -> Result<Dylib> {
265        MANAGER
266            .read()
267            .all
268            .get(shortname)
269            .filter(|lib| lib.deps().is_some())
270            .map(|lib| lib.get_dylib())
271            .ok_or(find_lib_error(format!("{}: load fail", shortname)))
272    }
273
274    /// Gets the name of the dependent libraries
275    pub fn needed_libs(&self) -> &[&'static str] {
276        self.dylib.needed_libs()
277    }
278
279    /// Gets the name of the dynamic library.
280    pub fn name(&self) -> &str {
281        self.dylib.name()
282    }
283
284    fn relocate_impl<F>(self, libs: &[Dylib], find: &'static F) -> Result<Dylib>
285    where
286        F: for<'b> Fn(&'b str) -> Option<*const ()>,
287    {
288        let mut deps = Vec::new();
289        deps.push(unsafe { RelocatedDylib::from_core_component(self.dylib.core_component()) });
290        deps.extend(libs.iter().map(|lib| lib.inner.clone()));
291        let deps = Arc::new(deps.into_boxed_slice());
292        let lazy_scope = create_lazy_scope(&deps, self.dylib.is_lazy());
293        let cur_lib = self
294            .dylib
295            .relocate(deps.clone().iter(), find, deal_unknown, lazy_scope)?;
296        if !self.flags.contains(OpenFlags::CUSTOM_NOT_REGISTER) {
297            register(
298                cur_lib.clone(),
299                self.flags,
300                Some(deps.clone()),
301                &mut MANAGER.write(),
302                *DylibState::default().set_relocated(),
303            );
304            Ok(Dylib {
305                inner: cur_lib,
306                flags: self.flags,
307                deps: Some(deps),
308            })
309        } else {
310            Ok(Dylib {
311                inner: cur_lib,
312                flags: self.flags,
313                deps: Some(deps),
314            })
315        }
316    }
317
318    /// Use libraries to relocate the current library.
319    /// # Examples
320    /// ```no_run
321    /// # use ::dlopen_rs::ElfLibrary;
322    /// let libc = ElfLibrary::load_existing("libc").unwrap();
323    /// let libgcc = ElfLibrary::load_existing("libgcc").unwrap();
324    /// let lib = ElfLibrary::from_file("/path/to/awesome.module", OpenFlags::RTLD_LOCAL)
325    /// 	.unwrap()
326    /// 	.relocate(&[libgcc, libc]);
327    /// ```
328    #[inline]
329    pub fn relocate(self, libs: impl AsRef<[Dylib]>) -> Result<Dylib> {
330        self.relocate_impl(libs.as_ref(), &|name| builtin::BUILTIN.get(name).copied())
331    }
332
333    /// Use libraries and function closure to relocate the current library.
334    /// # Examples
335    ///
336    /// ```no_run
337    /// # use ::dlopen_rs::ElfLibrary;
338    /// extern "C" fn mymalloc(size: size_t) -> *mut c_void {
339    ///     println!("malloc:{}bytes", size);
340    ///     unsafe { libc::malloc(size) }
341    /// }
342    /// let libc = ElfLibrary::load_existing("libc").unwrap();
343    /// let libgcc = ElfLibrary::load_existing("libgcc").unwrap();
344    /// let lib = ElfLibrary::from_file("/path/to/awesome.module", OpenFlags::RTLD_LOCAL)
345    /// 	.unwrap()
346    /// 	.relocate_with(&[libc, libgcc], &|name| {
347    ///         if name == "malloc" {
348    ///	             return Some(mymalloc as _);
349    ///         } else {
350    ///	             return None;
351    ///         }
352    ///     })
353    ///		.unwrap();
354    /// ```
355    /// # Note
356    /// It will use function closure to relocate current lib firstly.
357    #[inline]
358    pub fn relocate_with<F>(mut self, libs: impl AsRef<[Dylib]>, func: F) -> Result<Dylib>
359    where
360        F: for<'b> Fn(&'b str) -> Option<*const ()> + 'static,
361    {
362        type Closure = Box<dyn Fn(&str) -> Option<*const ()> + 'static>;
363
364        self.dylib.user_data_mut().unwrap().insert(
365            CLOSURE,
366            Box::new(
367                Box::new(move |name: &str| func(name).or(builtin::BUILTIN.get(name).copied()))
368                    as Closure,
369            ),
370        );
371        let func_ref: &Closure = unsafe {
372            core::mem::transmute(
373                self.dylib
374                    .user_data()
375                    .get(CLOSURE)
376                    .unwrap()
377                    .downcast_ref::<Closure>()
378                    .unwrap(),
379            )
380        };
381        self.relocate_impl(libs.as_ref(), func_ref)
382    }
383}
384
385/// An relocated dynamic library
386#[derive(Clone)]
387pub struct Dylib {
388    pub(crate) inner: RelocatedDylib<'static>,
389    pub(crate) flags: OpenFlags,
390    pub(crate) deps: Option<Arc<Box<[RelocatedDylib<'static>]>>>,
391}
392
393impl Debug for Dylib {
394    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
395        f.debug_struct("Dylib")
396            .field("inner", &self.inner)
397            .field("flags", &self.flags)
398            .finish()
399    }
400}
401
402impl Dylib {
403    /// Get the name of the dynamic library.
404    #[inline]
405    pub fn name(&self) -> &str {
406        self.inner.name()
407    }
408
409    /// Get the C-style name of the dynamic library.
410    #[inline]
411    pub fn cname(&self) -> &CStr {
412        self.inner.cname()
413    }
414
415    /// Get the base address of the dynamic library.
416    #[inline]
417    pub fn base(&self) -> usize {
418        self.inner.base()
419    }
420
421    /// Get the program headers of the dynamic library.
422    #[inline]
423    pub fn phdrs(&self) -> &[Phdr] {
424        self.inner.phdrs()
425    }
426
427    /// Get the needed libs' name of the elf object.
428    #[inline]
429    pub fn needed_libs(&self) -> &[&str] {
430        self.inner.needed_libs()
431    }
432
433    /// Get a pointer to a function or static variable by symbol name.
434    ///
435    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
436    /// most likely invalid.
437    ///
438    /// # Safety
439    /// Users of this API must specify the correct type of the function or variable loaded.
440    ///
441    /// # Examples
442    /// ```no_run
443    /// unsafe {
444    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
445    ///         lib.get("awesome_function").unwrap();
446    ///     awesome_function(0.42);
447    /// }
448    /// ```
449    /// A static variable may also be loaded and inspected:
450    /// ```no_run
451    /// unsafe {
452    ///     let awesome_variable: Symbol<*mut f64> = lib.get("awesome_variable").unwrap();
453    ///     **awesome_variable = 42.0;
454    /// };
455    /// ```
456    #[inline]
457    pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Result<Symbol<'lib, T>> {
458        find_symbol(self.deps.as_ref().unwrap(), name)
459    }
460
461    /// Load a versioned symbol from the dynamic library.
462    ///
463    /// # Examples
464    /// ```
465    /// let symbol = unsafe { lib.get_version::<fn()>>("function_name", "1.0").unwrap() };
466    /// ```
467    #[cfg(feature = "version")]
468    #[inline]
469    pub unsafe fn get_version<'lib, T>(
470        &'lib self,
471        name: &str,
472        version: &str,
473    ) -> Result<Symbol<'lib, T>> {
474        self.inner
475            .get_version(name, version)
476            .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
477    }
478}