Skip to main content

dyn_loader/
helpers.rs

1//! Shared `DynLib` wrapper and helpers used by both [`crate::dyn_mod`]
2//! (Rust fat-pointer bridge) and [`crate::cdyn`] (COM-style vtable loading).
3
4use std::path::Path;
5use std::sync::Arc;
6
7use anyhow::{Context, Result};
8use libloading::Library;
9
10/// Type of the entry point function that dyn plugins must export.
11pub type PluginEntryPointRaw = unsafe extern "C" fn() -> std::ffi::c_void;
12
13// ---------------------------------------------------------------------------
14// DynLib — Arc-shared dynamic library
15// ---------------------------------------------------------------------------
16
17#[derive(Clone)]
18pub struct DynLib {
19    library: Arc<Library>,
20    path: std::path::PathBuf,
21}
22
23impl DynLib {
24    /// Load a dynamic library from the given path.
25    ///
26    /// # Safety
27    ///
28    /// The target file must be a valid dynamic library for the current process.
29    pub unsafe fn load(path: &Path) -> Result<Self> {
30        let library = unsafe { Library::new(path) }
31            .with_context(|| format!("failed to load dynamic library: {}", path.display()))?;
32        Ok(Self {
33            library: Arc::new(library),
34            path: path.to_path_buf(),
35        })
36    }
37    pub fn path(&self) -> &Path {
38        &self.path
39    }
40
41    /// Load a symbol from the library.
42    ///
43    /// # Safety
44    ///
45    /// The caller must ensure `T` matches the actual exported symbol type.
46    pub unsafe fn symbol<T: Copy>(&self, name: &[u8]) -> Result<T> {
47        let sym = unsafe { self.library.get::<T>(name) }.with_context(|| {
48            format!(
49                "symbol '{}' not found in {}",
50                display_symbol(name),
51                self.path.display()
52            )
53        })?;
54        Ok(*sym)
55    }
56
57    /// Try to load a symbol; returns `None` if not found.
58    ///
59    /// # Safety
60    ///
61    /// The caller must ensure `T` matches the actual exported symbol type.
62    pub unsafe fn try_symbol<T: Copy>(&self, name: &[u8]) -> Option<T> {
63        unsafe { self.library.get::<T>(name) }.ok().map(|s| *s)
64    }
65}
66
67// ---------------------------------------------------------------------------
68// Helpers
69// ---------------------------------------------------------------------------
70
71pub(crate) fn display_symbol(symbol: &[u8]) -> String {
72    let end = symbol.iter().position(|&b| b == 0).unwrap_or(symbol.len());
73    String::from_utf8_lossy(&symbol[..end]).into_owned()
74}
75
76/// Quick check: does the file look like a plugin with the given entry symbol?
77///
78/// # Safety
79///
80/// Probes an arbitrary dynamic library for a specific exported symbol.
81pub unsafe fn looks_like_plugin(path: &Path, entry_symbol: &[u8]) -> bool {
82    match unsafe { DynLib::load(path) } {
83        Ok(lib) => unsafe { lib.try_symbol::<PluginEntryPointRaw>(entry_symbol) }.is_some(),
84        Err(_) => false,
85    }
86}