1use {
2 libloading::Symbol,
3 relib_internal_shared::Str,
4 std::{ffi::OsStr, path::Path},
5};
6
7mod errors;
8pub use errors::LoadError;
9
10#[cfg(feature = "unloading")]
11mod unloading;
12#[cfg(feature = "unloading")]
13pub use unloading::*;
14
15mod module;
16pub use module::Module;
17mod helpers;
18use helpers::{
19 LIBRARY_LOADING_GUARD, is_library_loaded, next_module_id, open_library, path_to_str,
20};
21mod leak_library;
22pub mod exports_types;
23pub use exports_types::{InitImports, ModuleExportsForHost};
24
25#[cfg(target_os = "windows")]
26mod windows;
27
28pub unsafe fn load_module<E: ModuleExportsForHost>(
72 path: impl AsRef<OsStr>,
73 init_imports: impl InitImports,
74) -> Result<Module<E>, crate::LoadError> {
75 unsafe {
76 load_module_with_options(
77 path,
78 init_imports,
79 #[cfg(feature = "unloading")]
80 true,
81 )
82 }
83}
84
85pub unsafe fn load_module_with_options<E: ModuleExportsForHost>(
90 path: impl AsRef<OsStr>,
91 init_imports: impl InitImports,
92
93 #[cfg(feature = "unloading")] enable_alloc_tracker: bool,
95) -> Result<Module<E>, crate::LoadError> {
96 let _loading_guard = LIBRARY_LOADING_GUARD
99 .lock()
100 .expect("Failed to lock library loading guard");
101
102 #[cfg(target_os = "windows")]
103 {
104 windows::dbghelp::try_init_from_load_module();
105 unsafe {
106 #[cfg(feature = "unloading")]
107 unloading::windows_thread_spawn_hook::init();
108 windows::enable_hooks();
109 }
110 }
111
112 let path = Path::new(path.as_ref());
113 let path_str = path_to_str(path);
114
115 if is_library_loaded(path_str) {
116 return Err(LoadError::ModuleAlreadyLoaded);
117 }
118
119 let library = open_library(path)?;
120
121 let module_comp_info = unsafe {
122 let compiled_with = library.get(b"__RELIB__CRATE_COMPILATION_INFO__\0");
123 let Ok(compiled_with) = compiled_with else {
124 return Err(LoadError::CouldNotGetCompilationInfo);
125 };
126 let compiled_with: Symbol<*const Str> = compiled_with;
127 let compiled_with: &Str = &**compiled_with;
128 compiled_with.to_string()
129 };
130
131 let host_comp_info = relib_internal_crate_compilation_info::get!();
132 if module_comp_info != host_comp_info {
133 return Err(LoadError::ModuleCompilationMismatch {
134 module: module_comp_info,
135 host: host_comp_info.to_owned(),
136 });
137 }
138
139 #[cfg(target_os = "windows")]
140 windows::dbghelp::add_module(path_str);
141
142 let module_id = next_module_id();
143
144 #[cfg(feature = "unloading")]
145 let internal_exports = {
146 unloading::init_internal_imports(&library);
147 unloading::module_allocs::add_module(module_id);
148
149 let internal_exports = unloading::InternalModuleExports::new(&library);
150 unsafe {
151 internal_exports.init(thread_id::get(), module_id, enable_alloc_tracker);
152 }
153 internal_exports
154 };
155
156 let pub_exports = E::new(&library);
157 init_imports.init(&library);
158
159 let module = Module::new(
160 module_id,
161 library,
162 pub_exports,
163 #[cfg(feature = "unloading")]
164 (internal_exports, path.to_owned(), enable_alloc_tracker),
165 );
166
167 #[cfg(all(target_os = "windows", feature = "unloading"))]
168 unloading::windows_thread_spawn_hook::add_module(module.library_handle);
169
170 Ok(module)
171}
172
173pub unsafe fn init() {
185 #[cfg(target_os = "windows")]
186 {
187 windows::dbghelp::try_init_standalone();
188 unsafe {
189 #[cfg(feature = "unloading")]
190 unloading::windows_thread_spawn_hook::init();
191 windows::enable_hooks();
192 }
193 }
194}
195
196#[cfg(any(target_os = "windows", relib_docs))]
203#[cfg(feature = "super_special_reinit_of_dbghelp")]
204pub unsafe fn forcibly_reinit_dbghelp() {
205 #[cfg(target_os = "windows")]
206 unsafe {
207 windows::dbghelp::forcibly_reinit_dbghelp();
208 windows::enable_hooks();
209 }
210}
211
212#[doc(hidden)]
214#[cfg(all(target_os = "windows", feature = "unloading"))]
215pub unsafe fn __suppress_unused_warning_for_linux_only_exports(
216 exports: unloading::InternalModuleExports,
217) {
218 unsafe {
219 exports.spawned_threads_count();
220 }
221}
222
223#[doc(hidden)]
224#[expect(unreachable_code)]
225#[cfg(all(target_os = "linux", feature = "unloading"))]
226pub unsafe fn __suppress_unused_warning_for_windows_only_exports(
227 exports: unloading::InternalModuleExports,
228) {
229 unsafe { exports.set_dealloc_callback(todo!()) }
230}