libloading/
safe.rs

1use core::{fmt, marker, ops};
2
3#[cfg(all(not(libloading_docs), target_os = "twizzler"))]
4use super::os::unix as imp;
5#[cfg(libloading_docs)]
6use super::os::unix as imp; // the implementation used here doesn't matter particularly much...
7#[cfg(all(not(libloading_docs), unix))]
8use super::os::unix as imp;
9#[cfg(all(not(libloading_docs), windows))]
10use super::os::windows as imp;
11use super::Error;
12use crate::{as_filename::AsFilename, as_symbol_name::AsSymbolName};
13
14/// A loaded dynamic library.
15#[cfg_attr(libloading_docs, doc(cfg(any(unix, windows))))]
16pub struct Library(imp::Library);
17
18impl Library {
19    /// Find and load a dynamic library.
20    ///
21    /// The `filename` argument may be either:
22    ///
23    /// * A library filename;
24    /// * The absolute path to the library;
25    /// * A relative (to the current working directory) path to the library.
26    ///
27    /// # Safety
28    ///
29    /// When a library is loaded, initialisation routines contained within it are executed.
30    /// For the purposes of safety, the execution of these routines is conceptually the same calling
31    /// an unknown foreign function and may impose arbitrary requirements on the caller for the
32    /// call to be sound.
33    ///
34    /// Additionally, the callers of this function must also ensure that execution of the
35    /// termination routines contained within the library is safe as well. These routines may be
36    /// executed when the library is unloaded.
37    ///
38    /// # Thread-safety
39    ///
40    /// The implementation strives to be as MT-safe as sanely possible, however on certain
41    /// platforms the underlying error-handling related APIs not always MT-safe. This library
42    /// shares these limitations on those platforms. In particular, on certain UNIX targets
43    /// `dlerror` is not MT-safe, resulting in garbage error messages in certain MT-scenarios.
44    ///
45    /// Calling this function from multiple threads is not MT-safe if used in conjunction with
46    /// library filenames and the library search path is modified (`SetDllDirectory` function on
47    /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
48    ///
49    /// # Platform-specific behaviour
50    ///
51    /// When a plain library filename is supplied, the locations in which the library is searched
52    /// are platform specific and cannot be adjusted in a portable manner. See the documentation
53    /// for the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`]
54    /// methods for further information on library lookup behaviour.
55    ///
56    /// If the `filename` specifies a library filename without a path and with the extension
57    /// omitted, the `.dll` extension is implicitly added on Windows.
58    ///
59    /// [`os::unix::Library::new`]: crate::os::unix::Library::new
60    /// [`os::windows::Library::new`]: crate::os::windows::Library::new
61    ///
62    /// # Tips
63    ///
64    /// Distributing your dynamic libraries under a filename common to all platforms (e.g.
65    /// `awesome.module`) allows you to avoid code which has to account for platform’s conventional
66    /// library filenames.
67    ///
68    /// Strive to specify an absolute or at least a relative path to your library, unless
69    /// system-wide libraries are being loaded. Platform-dependent library search locations
70    /// combined with various quirks related to path-less filenames may cause flakiness in
71    /// programs.
72    ///
73    /// # Examples
74    ///
75    /// ```no_run
76    /// # use ::libloading::Library;
77    /// // Any of the following are valid.
78    /// unsafe {
79    ///     let _ = Library::new("/path/to/awesome.module").unwrap();
80    ///     let _ = Library::new("../awesome.module").unwrap();
81    ///     let _ = Library::new("libsomelib.so.1").unwrap();
82    /// }
83    /// ```
84    pub unsafe fn new(filename: impl AsFilename) -> Result<Library, Error> {
85        imp::Library::new(filename).map(From::from)
86    }
87
88    /// Get a pointer to a function or static variable by symbol name.
89    ///
90    /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing
91    /// a null-terminated `symbol` may help to avoid an allocation.
92    ///
93    /// The symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y`
94    /// are most likely invalid.
95    ///
96    /// # Safety
97    ///
98    /// Users of this API must specify the correct type of the function or variable loaded.
99    ///
100    /// # Platform-specific behaviour
101    ///
102    /// The implementation of thread-local variables is extremely platform specific and uses of such
103    /// variables that work on e.g. Linux may have unintended behaviour on other targets.
104    ///
105    /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such
106    /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym`
107    /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null
108    /// pointer without it being an error. If loading a null pointer is something you care about,
109    /// consider using the [`os::unix::Library::get_singlethreaded`] call.
110    ///
111    /// [`os::unix::Library::get_singlethreaded`]: crate::os::unix::Library::get_singlethreaded
112    ///
113    /// # Examples
114    ///
115    /// Given a loaded library:
116    ///
117    /// ```no_run
118    /// # use ::libloading::Library;
119    /// let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
120    /// ```
121    ///
122    /// Loading and using a function looks like this:
123    ///
124    /// ```no_run
125    /// # use ::libloading::{Library, Symbol};
126    /// # let lib = unsafe {
127    /// #     Library::new("/path/to/awesome.module").unwrap()
128    /// # };
129    /// unsafe {
130    ///     let awesome_function: Symbol<unsafe extern "C" fn(f64) -> f64> =
131    ///         lib.get(b"awesome_function\0").unwrap();
132    ///     awesome_function(0.42);
133    /// }
134    /// ```
135    ///
136    /// A static variable may also be loaded and inspected:
137    ///
138    /// ```no_run
139    /// # use ::libloading::{Library, Symbol};
140    /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
141    /// unsafe {
142    ///     let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
143    ///     **awesome_variable = 42.0;
144    /// };
145    /// ```
146    pub unsafe fn get<T>(&self, symbol: impl AsSymbolName) -> Result<Symbol<'_, T>, Error> {
147        self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
148    }
149
150    /// Unload the library.
151    ///
152    /// This method might be a no-op, depending on the flags with which the `Library` was opened,
153    /// what library was opened or other platform specifics.
154    ///
155    /// You only need to call this if you are interested in handling any errors that may arise when
156    /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
157    /// library and ignore the errors were they arise.
158    ///
159    /// The underlying data structures may still get leaked if an error does occur.
160    pub fn close(self) -> Result<(), Error> {
161        self.0.close()
162    }
163}
164
165impl fmt::Debug for Library {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        self.0.fmt(f)
168    }
169}
170
171impl From<imp::Library> for Library {
172    fn from(lib: imp::Library) -> Library {
173        Library(lib)
174    }
175}
176
177impl From<Library> for imp::Library {
178    fn from(lib: Library) -> imp::Library {
179        lib.0
180    }
181}
182
183unsafe impl Send for Library {}
184unsafe impl Sync for Library {}
185
186/// Symbol from a library.
187///
188/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
189/// unloaded. The primary method to create an instance of a `Symbol` is via [`Library::get`].
190///
191/// The `Deref` trait implementation allows the use of `Symbol` as if it was a function or variable
192/// itself, without taking care to “extract” the function or variable manually most of the time.
193///
194/// [`Library::get`]: Library::get
195#[cfg_attr(libloading_docs, doc(cfg(any(unix, windows))))]
196pub struct Symbol<'lib, T: 'lib> {
197    inner: imp::Symbol<T>,
198    pd: marker::PhantomData<&'lib T>,
199}
200
201impl<'lib, T> Symbol<'lib, T> {
202    /// Extract the wrapped `os::platform::Symbol`.
203    ///
204    /// # Safety
205    ///
206    /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
207    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
208    /// was loaded from.
209    ///
210    /// # Examples
211    ///
212    /// ```no_run
213    /// # use ::libloading::{Library, Symbol};
214    /// unsafe {
215    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
216    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
217    ///     let symbol = symbol.into_raw();
218    /// }
219    /// ```
220    pub unsafe fn into_raw(self) -> imp::Symbol<T> {
221        self.inner
222    }
223
224    /// Wrap the `os::platform::Symbol` into this safe wrapper.
225    ///
226    /// Note that, in order to create association between the symbol and the library this symbol
227    /// came from, this function requires a reference to the library.
228    ///
229    /// # Safety
230    ///
231    /// The `library` reference must be exactly the library `sym` was loaded from.
232    ///
233    /// # Examples
234    ///
235    /// ```no_run
236    /// # use ::libloading::{Library, Symbol};
237    /// unsafe {
238    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
239    ///     let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
240    ///     let symbol = symbol.into_raw();
241    ///     let symbol = Symbol::from_raw(symbol, &lib);
242    /// }
243    /// ```
244    pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> {
245        let _ = library; // ignore here for documentation purposes.
246        Symbol {
247            inner: sym,
248            pd: marker::PhantomData,
249        }
250    }
251
252    /// Try to convert the symbol into a raw pointer.
253    /// Success depends on the platform. Currently, this fn always succeeds and returns some.
254    ///
255    /// # Safety
256    ///
257    /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
258    /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
259    /// was loaded from.
260    pub unsafe fn try_as_raw_ptr(self) -> Option<*mut core::ffi::c_void> {
261        Some(
262            unsafe {
263                // SAFE: the calling function has the same soundness invariants as this callee.
264                self.into_raw()
265            }
266            .as_raw_ptr(),
267        )
268    }
269}
270
271impl<'lib, T> Symbol<'lib, Option<T>> {
272    /// Lift Option out of the symbol.
273    ///
274    /// # Examples
275    ///
276    /// ```no_run
277    /// # use ::libloading::{Library, Symbol};
278    /// unsafe {
279    ///     let lib = Library::new("/path/to/awesome.module").unwrap();
280    ///     let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap();
281    ///     let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
282    /// }
283    /// ```
284    pub fn lift_option(self) -> Option<Symbol<'lib, T>> {
285        self.inner.lift_option().map(|is| Symbol {
286            inner: is,
287            pd: marker::PhantomData,
288        })
289    }
290}
291
292impl<'lib, T> Clone for Symbol<'lib, T> {
293    fn clone(&self) -> Symbol<'lib, T> {
294        Symbol {
295            inner: self.inner.clone(),
296            pd: marker::PhantomData,
297        }
298    }
299}
300
301// FIXME: implement FnOnce for callable stuff instead.
302impl<T> ops::Deref for Symbol<'_, T> {
303    type Target = T;
304    fn deref(&self) -> &T {
305        ops::Deref::deref(&self.inner)
306    }
307}
308
309impl<T> fmt::Debug for Symbol<'_, T> {
310    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
311        self.inner.fmt(f)
312    }
313}
314
315unsafe impl<T: Send> Send for Symbol<'_, T> {}
316unsafe impl<T: Sync> Sync for Symbol<'_, T> {}