nstd_sys/shared_lib.rs
1//! Access symbols from loaded shared libraries.
2//!
3//! # Platform support
4//!
5//! This module is only functional on Windows and Unix systems.
6#![cfg(any(unix, windows))]
7use crate::{
8 core::{optional::NSTDOptional, str::NSTDStr},
9 NSTDAny, NSTDAnyMut, NSTDChar,
10};
11use cfg_if::cfg_if;
12use nstdapi::nstdapi;
13
14cfg_if! {
15 if #[cfg(unix)] {
16 use crate::{
17 alloc::NSTD_ALLOCATOR,
18 core::{
19 cstr::{nstd_core_cstr_as_ptr, nstd_core_cstr_get_null},
20 str::nstd_core_str_as_cstr,
21 },
22 cstring::{nstd_cstring_as_ptr, nstd_cstring_from_cstr_unchecked},
23 };
24 use libc::{dlclose, dlopen, dlsym, RTLD_LAZY, RTLD_LOCAL};
25
26 /// A handle to a dynamically loaded library.
27 #[nstdapi]
28 pub struct NSTDSharedLib {
29 /// A raw handle to the shared library.
30 handle: NSTDAnyMut,
31 }
32 impl Drop for NSTDSharedLib {
33 /// [`NSTDSharedLib`]s destructor.
34 #[inline]
35 fn drop(&mut self) {
36 // SAFETY: `self.handle` is valid.
37 unsafe { dlclose(self.handle) };
38 }
39 }
40 // SAFETY: `NSTDSharedLib` owns a handle to the dynamically loaded library.
41 unsafe impl Send for NSTDSharedLib {}
42 // SAFETY: `NSTDSharedLib` does not undergo interior mutability.
43 unsafe impl Sync for NSTDSharedLib {}
44 } else if #[cfg(windows)] {
45 use crate::{
46 os::windows::{
47 shared_lib::{
48 nstd_os_windows_shared_lib_get, nstd_os_windows_shared_lib_get_mut,
49 nstd_os_windows_shared_lib_load, NSTDWindowsSharedLib,
50 },
51 str::nstd_os_windows_str_to_utf16,
52 },
53 vec::nstd_vec_as_ptr,
54 };
55
56 /// A handle to a dynamically loaded library.
57 pub type NSTDSharedLib = NSTDWindowsSharedLib;
58 }
59}
60
61/// An optional handle to a shared library.
62///
63/// This type is returned from `nstd_shared_lib_load`.
64pub type NSTDOptionalSharedLib = NSTDOptional<NSTDSharedLib>;
65
66/// Dynamically loads a shared library at runtime.
67///
68/// # Parameters:
69///
70/// - `const NSTDStr *path` - A path to the shared library.
71///
72/// # Returns
73///
74/// `NSTDOptionalSharedLib lib` - A handle to the dynamically loaded library, or none on error.
75///
76/// # Safety
77///
78/// - `path`'s data must be valid for reads.
79///
80/// - The loaded library may have platform-specific initialization routines ran when it is loaded.
81#[nstdapi]
82pub unsafe fn nstd_shared_lib_load(path: &NSTDStr) -> NSTDOptionalSharedLib {
83 #[cfg(unix)]
84 {
85 // Check if `path` is already null terminated.
86 let path = nstd_core_str_as_cstr(path);
87 if nstd_core_cstr_get_null(&path).is_null() {
88 // Allocate a null byte for `path`.
89 if let NSTDOptional::Some(path) =
90 nstd_cstring_from_cstr_unchecked(&NSTD_ALLOCATOR, &path)
91 {
92 let handle = dlopen(nstd_cstring_as_ptr(&path), RTLD_LAZY | RTLD_LOCAL);
93 if !handle.is_null() {
94 return NSTDOptional::Some(NSTDSharedLib { handle });
95 }
96 }
97 NSTDOptional::None
98 } else {
99 // Use the already null terminated `path`.
100 let handle = dlopen(nstd_core_cstr_as_ptr(&path), RTLD_LAZY | RTLD_LOCAL);
101 match !handle.is_null() {
102 true => NSTDOptional::Some(NSTDSharedLib { handle }),
103 false => NSTDOptional::None,
104 }
105 }
106 }
107 #[cfg(windows)]
108 {
109 let NSTDOptional::Some(utf16) = nstd_os_windows_str_to_utf16(path) else {
110 return NSTDOptional::None;
111 };
112 nstd_os_windows_shared_lib_load(nstd_vec_as_ptr(&utf16).cast())
113 }
114}
115
116/// Gets a pointer to a function or static variable in a dynamically loaded library by symbol name.
117///
118/// # Parameters
119///
120/// - `const NSTDSharedLib *lib` - The loaded library.
121///
122/// - `const NSTDChar *symbol` - The name of the function or variable to get a pointer to.
123///
124/// # Returns
125///
126/// `NSTDAny ptr` - A pointer to the function or variable.
127///
128/// # Safety
129///
130/// Undefined behavior may occur if `symbol`'s data is invalid.
131#[inline]
132#[nstdapi]
133pub unsafe fn nstd_shared_lib_get(lib: &NSTDSharedLib, symbol: *const NSTDChar) -> NSTDAny {
134 #[cfg(unix)]
135 return dlsym(lib.handle, symbol);
136 #[cfg(windows)]
137 return nstd_os_windows_shared_lib_get(lib, symbol);
138}
139
140/// Gets a mutable pointer to a function or static variable in a dynamically loaded library by
141/// symbol name.
142///
143/// # Parameters
144///
145/// - `NSTDSharedLib *lib` - The loaded library.
146///
147/// - `const NSTDChar *symbol` - The name of the function or variable to get a pointer to.
148///
149/// # Returns
150///
151/// `NSTDAnyMut ptr` - A pointer to the function or variable.
152///
153/// # Safety
154///
155/// Undefined behavior may occur if `symbol`'s data is invalid.
156#[inline]
157#[nstdapi]
158pub unsafe fn nstd_shared_lib_get_mut(
159 lib: &mut NSTDSharedLib,
160 symbol: *const NSTDChar,
161) -> NSTDAnyMut {
162 #[cfg(unix)]
163 return dlsym(lib.handle, symbol);
164 #[cfg(windows)]
165 return nstd_os_windows_shared_lib_get_mut(lib, symbol);
166}
167
168/// Unloads and frees the resources of a dynamically loaded library.
169///
170/// # Parameters:
171///
172/// - `NSTDSharedLib lib` - The library handle.
173#[inline]
174#[nstdapi]
175#[allow(
176 unused_variables,
177 clippy::missing_const_for_fn,
178 clippy::needless_pass_by_value
179)]
180pub fn nstd_shared_lib_free(lib: NSTDSharedLib) {}