Skip to content

Commit 7d4eb1f

Browse files
authored
0.4.2 (#19)
changelog in #19
1 parent b4cf2cc commit 7d4eb1f

File tree

12 files changed

+398
-199
lines changed

12 files changed

+398
-199
lines changed

host/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ description = "relib is a framework for reloadable dynamic libraries"
1010

1111
[features]
1212
unloading = ["relib_interface/unloading", "dep:thread-id"]
13+
super_special_reinit_of_dbghelp = []
1314

1415
[lints.clippy]
1516
unwrap_used = "forbid"
17+
[lints.rust]
18+
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(relib_docs)'] }
1619

1720
[package.metadata.docs.rs]
1821
all-features = true
19-
rustdoc-args = ["--generate-link-to-definition"]
22+
rustdoc-args = ["--generate-link-to-definition", "--cfg", "relib_docs"]
2023

2124
[dependencies]
2225
libloading.workspace = true

host/src/helpers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ mod windows_impl {
139139
pub fn is_library_loaded(library_path: &str) -> bool {
140140
let library_path = str_to_wide_cstring(library_path);
141141

142-
let mut module = 0;
142+
let mut module = std::ptr::null_mut();
143143
let flags = GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
144144
let r = unsafe { GetModuleHandleExW(flags, library_path.as_ptr(), &mut module) };
145145
r != 0

host/src/lib.rs

Lines changed: 176 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,176 @@
1-
use std::{ffi::OsStr, path::Path};
2-
3-
use libloading::Symbol;
4-
5-
use relib_internal_shared::Str;
6-
7-
mod errors;
8-
pub use errors::LoadError;
9-
10-
#[cfg(feature = "unloading")]
11-
mod unloading;
12-
#[cfg(feature = "unloading")]
13-
pub use unloading::*;
14-
15-
mod module;
16-
pub use module::Module;
17-
mod helpers;
18-
use helpers::{is_library_loaded, next_module_id, open_library, path_to_str};
19-
mod leak_library;
20-
pub mod exports_types;
21-
pub use exports_types::{ModuleExportsForHost, InitImports};
22-
23-
#[cfg(target_os = "windows")]
24-
mod windows;
25-
26-
/// Loads a module (dynamic library) by specified path.
27-
///
28-
/// # Safety
29-
/// This function is unsafe due to special patches related to backtraces on Windows,
30-
/// if you are on Linux ignore this safety condition:
31-
/// - Make sure you don't create backtraces
32-
/// (for example, by panic or using `std::backtrace`)
33-
/// in one thread and call this function **for the first time** from another one.
34-
///
35-
/// If you can't guarantee it when you call this function consider using
36-
/// [`init`] at the start of your program.
37-
///
38-
/// # Example
39-
/// ```
40-
/// let path_to_dylib = if cfg!(target_os = "linux") {
41-
/// "target/debug/libmodule.so"
42-
/// } else {
43-
/// "target/debug/module.dll"
44-
/// };
45-
///
46-
/// // `()` means empty imports and exports, module doesn't import or export anything
47-
/// let module = unsafe {
48-
/// relib_host::load_module::<()>(path_to_dylib, ())
49-
/// };
50-
/// let module = module.unwrap_or_else(|e| {
51-
/// panic!("module loading failed: {e:#}");
52-
/// });
53-
///
54-
/// // main function is unsafe to call (as well as any other module export) because these pre-conditions are not checked by relib:
55-
/// // - Returned value must be actually `R` at runtime. For example if you called this function with type bool but module returns i32, UB will occur.
56-
/// // - Type of return value must be FFI-safe.
57-
/// // - Returned value must not be a reference-counting pointer (see caveats in README or docs page).
58-
/// let returned_value = unsafe { module.call_main::<()>() };
59-
///
60-
/// // if module panics while executing any export it returns None
61-
/// // (panic will be printed by module)
62-
/// if returned_value.is_none() {
63-
/// println!("module panicked");
64-
/// }
65-
/// ```
66-
///
67-
/// # Panics
68-
/// Panics on Windows if `dbghelp.dll` was already loaded. See [`init`]
69-
pub unsafe fn load_module<E: ModuleExportsForHost>(
70-
path: impl AsRef<OsStr>,
71-
init_imports: impl InitImports,
72-
) -> Result<Module<E>, crate::LoadError> {
73-
#[cfg(target_os = "windows")]
74-
windows::dbghelp::try_init_from_load_module();
75-
76-
let path = Path::new(path.as_ref());
77-
let path_str = path_to_str(path);
78-
79-
if is_library_loaded(path_str) {
80-
return Err(LoadError::ModuleAlreadyLoaded);
81-
}
82-
83-
let library = open_library(path)?;
84-
85-
let module_comp_info = unsafe {
86-
let compiled_with = library.get(b"__RELIB__CRATE_COMPILATION_INFO__\0");
87-
let Ok(compiled_with) = compiled_with else {
88-
return Err(LoadError::CouldNotGetCompilationInfo);
89-
};
90-
let compiled_with: Symbol<*const Str> = compiled_with;
91-
let compiled_with: &Str = &**compiled_with;
92-
compiled_with.to_string()
93-
};
94-
95-
let host_comp_info = relib_internal_crate_compilation_info::get!();
96-
if module_comp_info != host_comp_info {
97-
return Err(LoadError::ModuleCompilationMismatch {
98-
module: module_comp_info,
99-
host: host_comp_info.to_owned(),
100-
});
101-
}
102-
103-
#[cfg(target_os = "windows")]
104-
windows::dbghelp::add_module(path_str);
105-
106-
let module_id = next_module_id();
107-
108-
#[cfg(feature = "unloading")]
109-
let internal_exports = {
110-
unloading::init_internal_imports(&library);
111-
unloading::module_allocs::add_module(module_id);
112-
113-
let internal_exports = unloading::InternalModuleExports::new(&library);
114-
unsafe {
115-
internal_exports.init(thread_id::get(), module_id);
116-
}
117-
internal_exports
118-
};
119-
120-
let pub_exports = E::new(&library);
121-
init_imports.init(&library);
122-
123-
let module = Module::new(
124-
module_id,
125-
library,
126-
pub_exports,
127-
#[cfg(feature = "unloading")]
128-
(internal_exports, path.to_owned()),
129-
);
130-
Ok(module)
131-
}
132-
133-
/// Currently, it's only needed for backtraces (for example, `std::backtrace::Backtrace`) to work correctly in modules on Windows.
134-
/// Doesn't actually do anything on Linux.
135-
/// Can be called before creating any backtraces if [`load_module`] panics due to already loaded `dbghelp.dll`.
136-
///
137-
/// # Safety
138-
/// Same as [`load_module`].
139-
///
140-
/// # Panics
141-
/// Panics on Windows if `dbghelp.dll` was already loaded (for example, by `backtrace` crate or standard library).
142-
pub unsafe fn init() {
143-
#[cfg(target_os = "windows")]
144-
windows::dbghelp::try_init();
145-
}
146-
147-
// TODO: fix it
148-
#[cfg(all(target_os = "windows", feature = "unloading"))]
149-
#[expect(clippy::missing_safety_doc)]
150-
pub unsafe fn __suppress_unused_warning_for_linux_only_exports(
151-
exports: unloading::InternalModuleExports,
152-
) {
153-
exports.spawned_threads_count();
154-
}
155-
156-
#[cfg(all(target_os = "linux", feature = "unloading"))]
157-
#[expect(clippy::missing_safety_doc)]
158-
pub unsafe fn __suppress_unused_warning_for_windows_only_exports(
159-
exports: unloading::InternalModuleExports,
160-
) {
161-
#[expect(unreachable_code)]
162-
exports.set_dealloc_callback(todo!());
163-
}
1+
use std::{ffi::OsStr, path::Path};
2+
3+
use libloading::Symbol;
4+
5+
use relib_internal_shared::Str;
6+
7+
mod errors;
8+
pub use errors::LoadError;
9+
10+
#[cfg(feature = "unloading")]
11+
mod unloading;
12+
#[cfg(feature = "unloading")]
13+
pub use unloading::*;
14+
15+
mod module;
16+
pub use module::Module;
17+
mod helpers;
18+
use helpers::{is_library_loaded, next_module_id, open_library, path_to_str};
19+
mod leak_library;
20+
pub mod exports_types;
21+
pub use exports_types::{ModuleExportsForHost, InitImports};
22+
23+
#[cfg(target_os = "windows")]
24+
mod windows;
25+
26+
/// Loads a module (dynamic library) by specified path.
27+
///
28+
/// # Safety
29+
/// This function is unsafe due to special patches related to backtraces on Windows,
30+
/// if you are on Linux ignore this safety condition:
31+
/// - Make sure you don't create backtraces
32+
/// (for example, by panic or using `std::backtrace`)
33+
/// in one thread and call this function **for the first time** from another one.
34+
///
35+
/// If you can't guarantee it when you call this function consider using
36+
/// [`init`] at the start of your program.
37+
///
38+
/// # Example
39+
/// ```
40+
/// let path_to_dylib = if cfg!(target_os = "linux") {
41+
/// "target/debug/libmodule.so"
42+
/// } else {
43+
/// "target/debug/module.dll"
44+
/// };
45+
///
46+
/// // `()` means empty imports and exports, module doesn't import or export anything
47+
/// let module = unsafe {
48+
/// relib_host::load_module::<()>(path_to_dylib, ())
49+
/// };
50+
/// let module = module.unwrap_or_else(|e| {
51+
/// panic!("module loading failed: {e:#}");
52+
/// });
53+
///
54+
/// // main function is unsafe to call (as well as any other module export) because these pre-conditions are not checked by relib:
55+
/// // - Returned value must be actually `R` at runtime. For example if you called this function with type bool but module returns i32, UB will occur.
56+
/// // - Type of return value must be FFI-safe.
57+
/// // - Returned value must not be a reference-counting pointer (see caveats in README or docs page).
58+
/// let returned_value = unsafe { module.call_main::<()>() };
59+
///
60+
/// // if module panics while executing any export it returns None
61+
/// // (panic will be printed by module)
62+
/// if returned_value.is_none() {
63+
/// println!("module panicked");
64+
/// }
65+
/// ```
66+
///
67+
/// # Panics
68+
/// Panics on Windows if `dbghelp.dll` was already loaded. See [`init`]
69+
pub unsafe fn load_module<E: ModuleExportsForHost>(
70+
path: impl AsRef<OsStr>,
71+
init_imports: impl InitImports,
72+
) -> Result<Module<E>, crate::LoadError> {
73+
#[cfg(target_os = "windows")]
74+
windows::dbghelp::try_init_from_load_module();
75+
76+
let path = Path::new(path.as_ref());
77+
let path_str = path_to_str(path);
78+
79+
if is_library_loaded(path_str) {
80+
return Err(LoadError::ModuleAlreadyLoaded);
81+
}
82+
83+
let library = open_library(path)?;
84+
85+
let module_comp_info = unsafe {
86+
let compiled_with = library.get(b"__RELIB__CRATE_COMPILATION_INFO__\0");
87+
let Ok(compiled_with) = compiled_with else {
88+
return Err(LoadError::CouldNotGetCompilationInfo);
89+
};
90+
let compiled_with: Symbol<*const Str> = compiled_with;
91+
let compiled_with: &Str = &**compiled_with;
92+
compiled_with.to_string()
93+
};
94+
95+
let host_comp_info = relib_internal_crate_compilation_info::get!();
96+
if module_comp_info != host_comp_info {
97+
return Err(LoadError::ModuleCompilationMismatch {
98+
module: module_comp_info,
99+
host: host_comp_info.to_owned(),
100+
});
101+
}
102+
103+
#[cfg(target_os = "windows")]
104+
windows::dbghelp::add_module(path_str);
105+
106+
let module_id = next_module_id();
107+
108+
#[cfg(feature = "unloading")]
109+
let internal_exports = {
110+
unloading::init_internal_imports(&library);
111+
unloading::module_allocs::add_module(module_id);
112+
113+
let internal_exports = unloading::InternalModuleExports::new(&library);
114+
unsafe {
115+
internal_exports.init(thread_id::get(), module_id);
116+
}
117+
internal_exports
118+
};
119+
120+
let pub_exports = E::new(&library);
121+
init_imports.init(&library);
122+
123+
let module = Module::new(
124+
module_id,
125+
library,
126+
pub_exports,
127+
#[cfg(feature = "unloading")]
128+
(internal_exports, path.to_owned()),
129+
);
130+
Ok(module)
131+
}
132+
133+
/// Currently, it's only needed for backtraces (for example, `std::backtrace::Backtrace`) to work correctly in modules on Windows.
134+
/// Doesn't actually do anything on Linux.
135+
/// Can be called before creating any backtraces if [`load_module`] panics due to already loaded `dbghelp.dll`.
136+
///
137+
/// # Safety
138+
/// Same as [`load_module`].
139+
///
140+
/// # Panics
141+
/// Panics on Windows if `dbghelp.dll` was already loaded (for example, by `backtrace` crate or standard library).
142+
pub unsafe fn init() {
143+
#[cfg(target_os = "windows")]
144+
windows::dbghelp::try_init_standalone();
145+
}
146+
147+
/// Don't use it unless you really need to.
148+
/// Forcibly terminates and reinitializes dbghelp.dll for backtraces on Windows.
149+
///
150+
/// # Safety
151+
/// God knows...
152+
///
153+
#[cfg(any(target_os = "windows", relib_docs))]
154+
#[cfg(feature = "super_special_reinit_of_dbghelp")]
155+
pub unsafe fn forcibly_reinit_dbghelp() {
156+
#[cfg(target_os = "windows")]
157+
windows::dbghelp::forcibly_reinit_dbghelp();
158+
}
159+
160+
// TODO: fix it
161+
#[cfg(all(target_os = "windows", feature = "unloading"))]
162+
#[expect(clippy::missing_safety_doc)]
163+
pub unsafe fn __suppress_unused_warning_for_linux_only_exports(
164+
exports: unloading::InternalModuleExports,
165+
) {
166+
exports.spawned_threads_count();
167+
}
168+
169+
#[cfg(all(target_os = "linux", feature = "unloading"))]
170+
#[expect(clippy::missing_safety_doc)]
171+
pub unsafe fn __suppress_unused_warning_for_windows_only_exports(
172+
exports: unloading::InternalModuleExports,
173+
) {
174+
#[expect(unreachable_code)]
175+
exports.set_dealloc_callback(todo!());
176+
}

host/src/windows.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub mod imports {
2424
pub const GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS: u32 = 0x00000004;
2525
pub const ERROR_INSUFFICIENT_BUFFER: u32 = 122;
2626

27-
windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleExW(flags: u32, module_name: *const u16, module: *mut isize) -> BOOL);
27+
windows_targets::link!("kernel32.dll" "system" fn GetModuleHandleExW(flags: u32, module_name: *const u16, module: *mut *mut isize) -> BOOL);
2828
windows_targets::link!("kernel32.dll" "system" fn GetModuleFileNameW(module: *const isize, file_name: PWSTR, size: DWORD) -> DWORD);
2929

3030
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
@@ -44,11 +44,11 @@ pub fn get_current_dylib() -> Option<PathBuf> {
4444
fn get_dylib_path(len: usize) -> Option<PathBuf> {
4545
let mut buf = Vec::with_capacity(len);
4646
unsafe {
47-
let module_handle = std::ptr::null_mut();
47+
let mut module_handle = std::ptr::null_mut();
4848
let flags =
4949
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
5050

51-
let failed = GetModuleHandleExW(flags, get_dylib_path as *const _, module_handle) == 0;
51+
let failed = GetModuleHandleExW(flags, get_dylib_path as *const _, &mut module_handle) == 0;
5252
if failed {
5353
None
5454
} else {

0 commit comments

Comments
 (0)