elf_loader/format/
dylib.rs

1use super::{ElfCommonPart, Relocated, create_lazy_scope};
2use crate::{
3    CoreComponent, Loader, Result, UserData,
4    arch::ElfPhdr,
5    dynamic::ElfDynamic,
6    loader::Builder,
7    mmap::Mmap,
8    object::{ElfObject, ElfObjectAsync},
9    parse_ehdr_error,
10    relocation::{LazyScope, SymDef, UnknownHandler, relocate_impl},
11    segment::ElfSegments,
12    symbol::{SymbolInfo, SymbolTable},
13};
14use alloc::{boxed::Box, ffi::CString, vec::Vec};
15use core::{fmt::Debug, marker::PhantomData, ops::Deref};
16
17/// An unrelocated dynamic library
18pub struct ElfDylib {
19    inner: ElfCommonPart,
20}
21
22impl Deref for ElfDylib {
23    type Target = ElfCommonPart;
24
25    fn deref(&self) -> &Self::Target {
26        &self.inner
27    }
28}
29
30impl Debug for ElfDylib {
31    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
32        f.debug_struct("ElfDylib")
33            .field("name", &self.inner.name())
34            .field("needed_libs", &self.inner.needed_libs())
35            .finish()
36    }
37}
38
39impl ElfDylib {
40    /// Gets mutable user data from the elf object.
41    #[inline]
42    pub fn user_data_mut(&mut self) -> Option<&mut UserData> {
43        self.inner.user_data_mut()
44    }
45
46    /// Relocate the dynamic library with the given dynamic libraries and function closure.
47    /// # Note
48    /// During relocation, the symbol is first searched in the function closure `pre_find`.
49    pub fn easy_relocate<'iter, 'scope, 'find, 'lib, F>(
50        self,
51        scope: impl IntoIterator<Item = &'iter RelocatedDylib<'scope>>,
52        pre_find: &'find F,
53    ) -> Result<RelocatedDylib<'lib>>
54    where
55        F: Fn(&str) -> Option<*const ()>,
56        'scope: 'iter,
57        'iter: 'lib,
58        'find: 'lib,
59    {
60        let iter = scope.into_iter();
61        let mut helper = Vec::new();
62        let local_lazy_scope = if self.is_lazy() {
63            let mut libs = Vec::new();
64            iter.for_each(|lib| {
65                libs.push(lib.downgrade());
66                helper.push(lib);
67            });
68            Some(create_lazy_scope(libs, pre_find))
69        } else {
70            iter.for_each(|lib| {
71                helper.push(lib);
72            });
73            None
74        };
75        self.relocate(
76            helper,
77            pre_find,
78            &mut |_, _, _| Err(Box::new(())),
79            local_lazy_scope,
80        )
81    }
82
83    /// Relocate the dynamic library with the given dynamic libraries and function closure.
84    /// # Note
85    /// * During relocation, the symbol is first searched in the function closure `pre_find`.
86    /// * The `deal_unknown` function is used to handle relocation types not implemented by efl_loader or failed relocations
87    /// * Typically, the `scope` should also contain the current dynamic library itself,
88    ///   relocation will be done in the exact order in which the dynamic libraries appear in `scope`.
89    /// * When lazy binding, the symbol is first looked for in the global scope and then in the local lazy scope
90    pub fn relocate<'iter, 'scope, 'find, 'lib, F>(
91        self,
92        scope: impl AsRef<[&'iter RelocatedDylib<'scope>]>,
93        pre_find: &'find F,
94        deal_unknown: &mut UnknownHandler,
95        local_lazy_scope: Option<LazyScope<'lib>>,
96    ) -> Result<RelocatedDylib<'lib>>
97    where
98        F: Fn(&str) -> Option<*const ()>,
99        'scope: 'iter,
100        'iter: 'lib,
101        'find: 'lib,
102    {
103        Ok(RelocatedDylib {
104            inner: relocate_impl(
105                self.inner,
106                scope.as_ref(),
107                pre_find,
108                deal_unknown,
109                local_lazy_scope,
110            )?,
111        })
112    }
113}
114
115impl Builder {
116    pub(crate) fn create_dylib(self, phdrs: &[ElfPhdr]) -> ElfDylib {
117        let inner = self.create_inner(phdrs, true);
118        ElfDylib { inner }
119    }
120}
121
122impl<M: Mmap> Loader<M> {
123    /// Load a dynamic library into memory
124    pub fn easy_load_dylib(&mut self, object: impl ElfObject) -> Result<ElfDylib> {
125        self.load_dylib(object, None)
126    }
127
128    /// Load a dynamic library into memory
129    /// # Note
130    /// When `lazy_bind` is not set, lazy binding is enabled using the dynamic library's DT_FLAGS flag.
131    pub fn load_dylib(
132        &mut self,
133        mut object: impl ElfObject,
134        lazy_bind: Option<bool>,
135    ) -> Result<ElfDylib> {
136        let ehdr = self.buf.prepare_ehdr(&mut object)?;
137        if !ehdr.is_dylib() {
138            return Err(parse_ehdr_error("file type mismatch"));
139        }
140        let (builder, phdrs) = self.load_impl(ehdr, object, lazy_bind)?;
141        Ok(builder.create_dylib(phdrs))
142    }
143
144    /// Load a dynamic library into memory
145    /// # Note
146    /// When `lazy_bind` is not set, lazy binding is enabled using the dynamic library's DT_FLAGS flag.
147    pub async fn load_dylib_async(
148        &mut self,
149        mut object: impl ElfObjectAsync,
150        lazy_bind: Option<bool>,
151    ) -> Result<ElfDylib> {
152        let ehdr = self.buf.prepare_ehdr(&mut object)?;
153        if !ehdr.is_dylib() {
154            return Err(parse_ehdr_error("file type mismatch"));
155        }
156        let (builder, phdrs) = self.load_async_impl(ehdr, object, lazy_bind).await?;
157        Ok(builder.create_dylib(phdrs))
158    }
159}
160
161/// A dynamic library that has been relocated
162#[derive(Clone)]
163pub struct RelocatedDylib<'scope> {
164    inner: Relocated<'scope>,
165}
166
167impl Debug for RelocatedDylib<'_> {
168    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
169        self.inner.fmt(f)
170    }
171}
172
173impl Deref for RelocatedDylib<'_> {
174    type Target = CoreComponent;
175
176    fn deref(&self) -> &Self::Target {
177        &self.inner
178    }
179}
180
181impl RelocatedDylib<'_> {
182    /// # Safety
183    /// The current elf object has not yet been relocated, so it is dangerous to use this
184    /// function to convert `CoreComponent` to `RelocateDylib`. And lifecycle information is lost
185    #[inline]
186    pub unsafe fn from_core_component(core: CoreComponent) -> Self {
187        RelocatedDylib {
188            inner: Relocated {
189                core,
190                _marker: PhantomData,
191            },
192        }
193    }
194
195    /// Gets the core component reference of the elf object.
196    /// # Safety
197    /// Lifecycle information is lost, and the dependencies of the current elf object can be prematurely deallocated,
198    /// which can cause serious problems.
199    #[inline]
200    pub unsafe fn core_component_ref(&self) -> &CoreComponent {
201        &self.inner
202    }
203
204    /// # Safety
205    /// The caller needs to ensure that the parameters passed in come from a valid dynamic library.
206    #[inline]
207    pub unsafe fn new_uncheck(
208        name: CString,
209        base: usize,
210        dynamic: ElfDynamic,
211        phdrs: &'static [ElfPhdr],
212        segments: ElfSegments,
213        user_data: UserData,
214    ) -> Self {
215        Self {
216            inner: Relocated {
217                core: CoreComponent::from_raw(name, base, dynamic, phdrs, segments, user_data),
218                _marker: PhantomData,
219            },
220        }
221    }
222
223    /// Gets the symbol table.
224    #[inline]
225    pub fn symtab(&self) -> &SymbolTable {
226        unsafe { self.inner.symtab().unwrap_unchecked() }
227    }
228
229    /// Gets a pointer to a function or static variable by symbol name.
230    ///
231    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
232    /// most likely invalid.
233    ///
234    /// # Safety
235    /// Users of this API must specify the correct type of the function or variable loaded.
236    ///
237    /// # Examples
238    /// ```no_run
239    /// # use elf_loader::{object::ElfBinary, Symbol, mmap::MmapImpl, Loader};
240    /// # let mut loader = Loader::<MmapImpl>::new();
241    /// # let lib = loader
242    /// #     .easy_load_dylib(ElfBinary::new("target/liba.so", &[]))
243    /// #        .unwrap().easy_relocate([].iter(), &|_|{None}).unwrap();
244    /// unsafe {
245    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
246    ///         lib.get("awesome_function").unwrap();
247    ///     awesome_function(0.42);
248    /// }
249    /// ```
250    /// A static variable may also be loaded and inspected:
251    /// ```no_run
252    /// # use elf_loader::{object::ElfBinary, Symbol, mmap::MmapImpl, Loader};
253    /// # let mut loader = Loader::<MmapImpl>::new();
254    /// # let lib = loader
255    /// #     .easy_load_dylib(ElfBinary::new("target/liba.so", &[]))
256    /// #        .unwrap().easy_relocate([].iter(), &|_|{None}).unwrap();
257    /// unsafe {
258    ///     let awesome_variable: Symbol<*mut f64> = lib.get("awesome_variable").unwrap();
259    ///     **awesome_variable = 42.0;
260    /// };
261    /// ```
262    #[inline]
263    pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Option<Symbol<'lib, T>> {
264        let syminfo = SymbolInfo::from_str(name, None);
265        let mut precompute = syminfo.precompute();
266        self.symtab()
267            .lookup_filter(&syminfo, &mut precompute)
268            .map(|sym| Symbol {
269                ptr: SymDef {
270                    sym: Some(sym),
271                    lib: self,
272                }
273                .convert() as _,
274                pd: PhantomData,
275            })
276    }
277
278    /// Load a versioned symbol from the elf object.
279    /// # Safety
280    /// Users of this API must specify the correct type of the function or variable loaded.
281    /// # Examples
282    /// ```no_run
283    /// # use elf_loader::{object::ElfFile, Symbol, mmap::MmapImpl, Loader};
284    /// # let mut loader = Loader::<MmapImpl>::new();
285    /// # let lib = loader
286    /// #     .easy_load_dylib(ElfFile::from_path("target/liba.so").unwrap())
287    /// #        .unwrap().easy_relocate([].iter(), &|_|{None}).unwrap();;
288    /// let symbol = unsafe { lib.get_version::<fn()>("function_name", "1.0").unwrap() };
289    /// ```
290    #[cfg(feature = "version")]
291    #[inline]
292    pub unsafe fn get_version<'lib, T>(
293        &'lib self,
294        name: &str,
295        version: &str,
296    ) -> Option<Symbol<'lib, T>> {
297        let syminfo = SymbolInfo::from_str(name, Some(version));
298        let mut precompute = syminfo.precompute();
299        self.symtab()
300            .lookup_filter(&syminfo, &mut precompute)
301            .map(|sym| Symbol {
302                ptr: SymDef {
303                    sym: Some(sym),
304                    lib: self,
305                }
306                .convert() as _,
307                pd: PhantomData,
308            })
309    }
310}
311
312/// A symbol from elf object
313#[derive(Debug, Clone)]
314pub struct Symbol<'lib, T: 'lib> {
315    ptr: *mut (),
316    pd: PhantomData<&'lib T>,
317}
318
319impl<'lib, T> Deref for Symbol<'lib, T> {
320    type Target = T;
321    fn deref(&self) -> &T {
322        unsafe { &*(&self.ptr as *const *mut _ as *const T) }
323    }
324}
325
326impl<'lib, T> Symbol<'lib, T> {
327    pub fn into_raw(self) -> *const () {
328        self.ptr
329    }
330}
331
332unsafe impl<T: Send> Send for Symbol<'_, T> {}
333unsafe impl<T: Sync> Sync for Symbol<'_, T> {}