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