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) {}