Skip to main content

dlopen_rs/core_impl/
loader.rs

1use crate::core_impl::register::global_find;
2use crate::core_impl::traits::AsFilename;
3use crate::core_impl::types::{ARGC, ARGV, ENVP, ExtraData, LinkMap};
4use crate::utils::debug::add_debug_link_map;
5use crate::utils::linker_script::get_linker_script_libs;
6use crate::{OpenFlags, Result, error::find_symbol_error};
7use alloc::{
8    boxed::Box,
9    ffi::CString,
10    format,
11    string::{String, ToString},
12    sync::Arc,
13    vec::Vec,
14};
15use core::{
16    ffi::{c_char, c_int},
17    fmt::Debug,
18};
19use elf_loader::input::ElfFile;
20use elf_loader::loader::LifecycleContext;
21use elf_loader::{
22    Loader,
23    elf::{ElfDyn, ElfPhdr, abi::PT_DYNAMIC},
24    image::{ElfCoreRef, LoadedCore, RawDylib, Symbol},
25    input::{ElfBinary, ElfReader},
26};
27
28pub(crate) type ElfDylib = RawDylib<ExtraData>;
29pub(crate) type LoadedDylib = LoadedCore<ExtraData>;
30pub(crate) type CoreComponentRef = ElfCoreRef<ExtraData>;
31
32pub(crate) enum LoadResult {
33    Dylib(ElfDylib),
34    Script(Vec<String>),
35}
36
37/// Searches for a symbol in a list of relocated libraries.
38///
39/// Iterates through the provided libraries in order and returns the first matching symbol.
40#[inline]
41pub(crate) fn find_symbol<'lib, T>(
42    libs: &'lib [LoadedDylib],
43    name: &str,
44) -> Result<Symbol<'lib, T>> {
45    log::info!("Get the symbol [{}] in [{}]", name, libs[0].name());
46    libs.iter()
47        .find_map(|lib| unsafe { lib.get::<T>(name) })
48        .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
49}
50
51/// Creates a closure for lazy symbol resolution.
52///
53/// The closure captures weak references to the library's dependencies and uses them
54/// to resolve symbols at runtime. It respects the `RTLD_DEEPBIND` flag to determine
55/// the search order between the local dependency scope and the global scope.
56#[inline]
57pub(crate) fn create_lazy_scope(
58    deps: &[LoadedDylib],
59    flags: OpenFlags,
60) -> Arc<dyn for<'a> Fn(&'a str) -> Option<*const ()> + Send + Sync + 'static> {
61    let deps_weak: Vec<CoreComponentRef> = deps
62        .iter()
63        .map(|dep| unsafe { dep.core_ref().downgrade() })
64        .collect();
65    Arc::new(move |name: &str| {
66        let deepbind = flags.is_deepbind();
67
68        let local_find = || {
69            deps_weak.iter().find_map(|dep| unsafe {
70                let core = dep.upgrade()?;
71                let lib = LoadedDylib::from_core(core);
72                lib.get::<()>(name).map(|sym| {
73                    log::trace!(
74                        "Lazy Binding: find symbol [{}] from [{}] in local scope ",
75                        name,
76                        lib.name()
77                    );
78                    let val = sym.into_raw();
79                    assert!(lib.base() != val as usize);
80                    val
81                })
82            })
83        };
84
85        if deepbind {
86            local_find().or_else(|| unsafe { global_find::<()>(name).map(|s| s.into_raw()) })
87        } else {
88            unsafe { global_find::<()>(name) }
89                .map(|s| s.into_raw())
90                .or_else(local_find)
91        }
92    })
93}
94
95fn from_impl<'a, I>(object: I) -> Result<ElfDylib>
96where
97    I: ElfReader + elf_loader::input::IntoElfReader<'a>,
98{
99    let mut dylib = Loader::new()
100        .with_default_tls_resolver()
101        .with_context::<ExtraData>()
102        .with_init(|ctx: &LifecycleContext| {
103            let argc = unsafe { *core::ptr::addr_of!(ARGC) };
104            let argv = unsafe { *core::ptr::addr_of!(ARGV) };
105            let envp = unsafe { *core::ptr::addr_of!(ENVP) as *const *mut c_char };
106            type InitFn = unsafe extern "C" fn(c_int, *const *mut c_char, *const *mut c_char);
107            if let Some(init) = ctx.func() {
108                let init: InitFn = unsafe { core::mem::transmute(init) };
109                unsafe { init(argc as c_int, argv, envp) };
110            }
111            if let Some(init_array) = ctx.func_array() {
112                for &f in init_array {
113                    let f: InitFn = unsafe { core::mem::transmute(f) };
114                    unsafe { f(argc as c_int, argv, envp) };
115                }
116            }
117        })
118        .load_dylib(object)?;
119    let needed_libs = dylib
120        .needed_libs()
121        .iter()
122        .map(|s| s.to_string())
123        .collect::<Vec<_>>();
124
125    let name = dylib.name().to_string();
126    let base = dylib.base();
127    let dynamic_ptr = dylib
128        .phdrs()
129        .iter()
130        .find(|p| p.p_type == PT_DYNAMIC)
131        .map(|p| (base + p.p_vaddr as usize) as *mut ElfDyn)
132        .unwrap_or(core::ptr::null_mut());
133
134    let user_data = dylib.user_data_mut().unwrap();
135    user_data.needed_libs = needed_libs;
136    let c_name = CString::new(name).unwrap();
137
138    let mut link_map = Box::new(LinkMap {
139        l_addr: base as *mut _,
140        l_name: c_name.as_ptr(),
141        l_ld: dynamic_ptr as *mut _,
142        l_next: core::ptr::null_mut(),
143        l_prev: core::ptr::null_mut(),
144    });
145
146    unsafe { add_debug_link_map(link_map.as_mut()) };
147    user_data.link_map = Some(link_map);
148    user_data.c_name = Some(c_name);
149    Ok(dylib)
150}
151
152/// Represents a successfully loaded and relocated dynamic library.
153///
154/// This is the primary interface for interacting with a loaded library,
155/// providing methods to look up symbols and inspect metadata.
156#[derive(Clone)]
157pub struct ElfLibrary {
158    pub(crate) inner: LoadedDylib,
159    /// The flattened dependency scope (Searchlist) used by this library.
160    pub(crate) deps: Option<Arc<[LoadedDylib]>>,
161}
162
163impl Debug for ElfLibrary {
164    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
165        f.debug_struct("Dylib").field("inner", &self.inner).finish()
166    }
167}
168
169impl ElfLibrary {
170    pub(crate) fn load(path: &str, content: Option<&[u8]>) -> Result<LoadResult> {
171        if let Some(bytes) = content {
172            if bytes.starts_with(b"\x7fELF") {
173                let dylib = Self::from_binary(bytes, path)?;
174                Ok(LoadResult::Dylib(dylib))
175            } else {
176                let libs = get_linker_script_libs(bytes);
177                Ok(LoadResult::Script(libs))
178            }
179        } else {
180            let header = crate::os::read_file_limit(path, 64)?;
181            if header.starts_with(b"\x7fELF") {
182                let dylib = Self::from_file(path)?;
183                Ok(LoadResult::Dylib(dylib))
184            } else {
185                let content = crate::os::read_file(path)?;
186                let libs = get_linker_script_libs(&content);
187                Ok(LoadResult::Script(libs))
188            }
189        }
190    }
191
192    fn from_file(path: impl AsFilename) -> Result<ElfDylib> {
193        let path_ref = path.as_filename();
194        let file = ElfFile::from_path(path_ref)?;
195        from_impl(file)
196    }
197
198    /// Load a elf dynamic library from bytes.
199    fn from_binary(bytes: &[u8], path: impl AsFilename) -> Result<ElfDylib> {
200        let file = ElfBinary::new(path.as_filename(), bytes);
201        from_impl(file)
202    }
203}
204
205pub trait DylibExt {
206    fn needed_libs(&self) -> &[String];
207    fn shortname(&self) -> &str;
208}
209
210impl DylibExt for LoadedDylib {
211    #[inline]
212    fn needed_libs(&self) -> &[String] {
213        &self.user_data().needed_libs
214    }
215
216    #[inline]
217    fn shortname(&self) -> &str {
218        let name = self.name();
219        if name.is_empty() {
220            "main"
221        } else {
222            self.short_name()
223        }
224    }
225}
226
227impl ElfLibrary {
228    /// Get the name of the dynamic library.
229    #[inline]
230    pub fn name(&self) -> &str {
231        self.inner.name()
232    }
233
234    /// Get the C-style name of the dynamic library.
235    #[inline]
236    pub fn cname(&self) -> *const c_char {
237        self.inner
238            .user_data()
239            .c_name
240            .as_ref()
241            .map(|n| n.as_ptr())
242            .unwrap_or(core::ptr::null())
243    }
244
245    /// Get the short name of the dynamic library.
246    #[inline]
247    pub fn shortname(&self) -> &str {
248        self.inner.shortname()
249    }
250
251    /// Get the current flags of the dynamic library from the global registry.
252    pub fn flags(&self) -> OpenFlags {
253        use crate::core_impl::register::MANAGER;
254        crate::lock_read!(MANAGER)
255            .get(self.shortname())
256            .map(|e| e.flags)
257            .unwrap_or(OpenFlags::empty())
258    }
259
260    /// Get the base address of the dynamic library.
261    #[inline]
262    pub fn base(&self) -> usize {
263        self.inner.base()
264    }
265
266    /// Gets the memory length of the elf object map.
267    #[inline]
268    pub fn mapped_len(&self) -> usize {
269        self.inner.mapped_len()
270    }
271
272    /// Get the program headers of the dynamic library.
273    #[inline]
274    pub fn phdrs(&self) -> Option<&[ElfPhdr]> {
275        self.inner.phdrs()
276    }
277
278    /// Get the needed libs' name of the elf object.
279    #[inline]
280    pub fn needed_libs(&self) -> &[String] {
281        self.inner.needed_libs()
282    }
283
284    /// Get a pointer to a function or static variable by symbol name.
285    ///
286    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
287    /// most likely invalid.
288    ///
289    /// # Safety
290    /// Users of this API must specify the correct type of the function or variable loaded.
291    ///
292    /// # Examples
293    /// ```no_run
294    /// # use dlopen_rs::{Symbol, ElfLibrary ,OpenFlags};
295    /// # let lib = ElfLibrary::dlopen("awesome.so", OpenFlags::RTLD_NOW).unwrap();
296    /// unsafe {
297    ///     let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
298    ///         lib.get("awesome_function").unwrap();
299    ///     awesome_function(0.42);
300    /// }
301    /// ```
302    /// A static variable may also be loaded and inspected:
303    /// ```no_run
304    /// # use dlopen_rs::{Symbol, ElfLibrary ,OpenFlags};
305    /// # let lib = ElfLibrary::dlopen("awesome.so", OpenFlags::RTLD_NOW).unwrap();
306    /// unsafe {
307    ///     let awesome_variable: Symbol<*mut f64> = lib.get("awesome_variable").unwrap();
308    ///     **awesome_variable = 42.0;
309    /// };
310    /// ```
311    #[inline]
312    pub unsafe fn get<'lib, T>(&'lib self, name: &str) -> Result<Symbol<'lib, T>> {
313        find_symbol(self.deps.as_ref().unwrap(), name)
314    }
315
316    /// Load a versioned symbol from the dynamic library.
317    ///
318    /// # Examples
319    /// ```no_run
320    /// # use dlopen_rs::{Symbol, ElfLibrary ,OpenFlags};
321    /// # let lib = ElfLibrary::dlopen("awesome.so", OpenFlags::RTLD_NOW).unwrap();
322    /// let symbol = unsafe { lib.get_version::<fn()>("function_name", "1.0").unwrap() };
323    /// ```
324    #[cfg(feature = "version")]
325    #[inline]
326    pub unsafe fn get_version<'lib, T>(
327        &'lib self,
328        name: &str,
329        version: &str,
330    ) -> Result<Symbol<'lib, T>> {
331        unsafe {
332            self.inner
333                .get_version(name, version)
334                .ok_or(find_symbol_error(format!("can not find symbol:{}", name)))
335        }
336    }
337}