dynpatch_core/
loader.rs

1//! Dynamic library loading and symbol resolution
2
3use crate::error::{Error, Result};
4use dynpatch_interface::PatchMetadata;
5use libloading::{Library as LibloadingLibrary, Symbol};
6use std::path::{Path, PathBuf};
7use std::sync::Arc;
8use tracing::{debug, info};
9
10/// A loaded patch library
11pub struct Library {
12    _lib: Arc<LibloadingLibrary>,
13    path: PathBuf,
14    metadata: PatchMetadata,
15}
16
17impl Library {
18    /// Load a patch library from the specified path
19    pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
20        let path = path.as_ref();
21        debug!("Loading library from: {:?}", path);
22
23        let lib = unsafe {
24            LibloadingLibrary::new(path).map_err(|e| Error::LibraryLoadError {
25                path: path.to_path_buf(),
26                source: e,
27            })?
28        };
29
30        let lib = Arc::new(lib);
31
32        // Load metadata
33        let metadata = unsafe {
34            let get_metadata: Symbol<fn() -> PatchMetadata> = lib
35                .get(b"__dynpatch_metadata\0")
36                .map_err(|_| Error::SymbolNotFound {
37                    symbol: "__dynpatch_metadata".to_string(),
38                })?;
39            get_metadata()
40        };
41
42        info!(
43            "Loaded patch: {} v{} (interface v{})",
44            metadata.name, metadata.version, metadata.interface_version
45        );
46
47        Ok(Self {
48            _lib: lib,
49            path: path.to_path_buf(),
50            metadata,
51        })
52    }
53
54    /// Get the patch metadata
55    pub fn metadata(&self) -> &PatchMetadata {
56        &self.metadata
57    }
58
59    /// Get the library path
60    pub fn path(&self) -> &Path {
61        &self.path
62    }
63
64    /// Resolve a symbol by name
65    ///
66    /// # Safety
67    /// 
68    /// The caller must ensure that the symbol has the correct type signature
69    /// and that the library remains loaded for the lifetime of the symbol.
70    pub unsafe fn get_symbol<T>(&self, name: &[u8]) -> Result<Symbol<T>> {
71        self._lib.get(name).map_err(|_| Error::SymbolNotFound {
72            symbol: String::from_utf8_lossy(name).to_string(),
73        })
74    }
75
76    /// Try to get the entry point function and execute it
77    pub fn call_entry_point(&self) -> Result<()> {
78        unsafe {
79            match self.get_symbol::<fn() -> i32>(b"__dynpatch_entry\0") {
80                Ok(entry) => {
81                    let result = entry();
82                    if result != 0 {
83                        return Err(Error::InitializationFailed {
84                            code: result,
85                            message: "Patch entry point returned non-zero".to_string(),
86                        });
87                    }
88                    info!("Patch entry point executed successfully");
89                    Ok(())
90                }
91                Err(_) => {
92                    debug!("No entry point found, skipping initialization");
93                    Ok(())
94                }
95            }
96        }
97    }
98
99    /// Check if the library has a specific symbol
100    pub fn has_symbol(&self, name: &[u8]) -> bool {
101        unsafe { self._lib.get::<*const ()>(name).is_ok() }
102    }
103}
104
105impl std::fmt::Debug for Library {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_struct("Library")
108            .field("path", &self.path)
109            .field("metadata", &self.metadata)
110            .finish()
111    }
112}
113
114/// Loader for patch libraries
115pub struct PatchLoader {
116    validation_enabled: bool,
117}
118
119impl PatchLoader {
120    pub fn new() -> Self {
121        Self {
122            validation_enabled: true,
123        }
124    }
125
126    pub fn with_validation(mut self, enabled: bool) -> Self {
127        self.validation_enabled = enabled;
128        self
129    }
130
131    /// Load a patch library and optionally validate it
132    pub fn load<P: AsRef<Path>>(&self, path: P) -> Result<Library> {
133        let library = Library::load(path)?;
134
135        if self.validation_enabled {
136            debug!("Validation enabled, running checks");
137            // Basic validation happens in validator module
138        }
139
140        Ok(library)
141    }
142}
143
144impl Default for PatchLoader {
145    fn default() -> Self {
146        Self::new()
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use super::*;
153
154    #[test]
155    fn test_loader_creation() {
156        let loader = PatchLoader::new();
157        assert!(loader.validation_enabled);
158
159        let loader = PatchLoader::new().with_validation(false);
160        assert!(!loader.validation_enabled);
161    }
162}