1use 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
10pub struct Library {
12 _lib: Arc<LibloadingLibrary>,
13 path: PathBuf,
14 metadata: PatchMetadata,
15}
16
17impl Library {
18 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 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 pub fn metadata(&self) -> &PatchMetadata {
56 &self.metadata
57 }
58
59 pub fn path(&self) -> &Path {
61 &self.path
62 }
63
64 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 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 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
114pub 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 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 }
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}