libloading/safe.rs
1#[cfg(libloading_docs)]
2use super::os::unix as imp; // the implementation used here doesn't matter particularly much...
3#[cfg(all(not(libloading_docs), unix))]
4use super::os::unix as imp;
5#[cfg(all(not(libloading_docs), windows))]
6use super::os::windows as imp;
7use super::Error;
8use std::ffi::OsStr;
9use std::fmt;
10use std::marker;
11use std::ops;
12use std::os::raw;
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 an
31 /// unknown foreign function and may impose arbitrary requirements on the caller for the call
32 /// 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 are
52 /// platform specific and cannot be adjusted in a portable manner. See the documentation for
53 /// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods
54 /// for further information on library lookup behaviour.
55 ///
56 /// If the `filename` specifies a library filename without a path and with the extension omitted,
57 /// 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<P: AsRef<OsStr>>(filename: P) -> 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 a
91 /// 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` are
94 /// 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 {
120 /// Library::new("/path/to/awesome.module").unwrap()
121 /// };
122 /// ```
123 ///
124 /// Loading and using a function looks like this:
125 ///
126 /// ```no_run
127 /// # use ::libloading::{Library, Symbol};
128 /// # let lib = unsafe {
129 /// # Library::new("/path/to/awesome.module").unwrap()
130 /// # };
131 /// unsafe {
132 /// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
133 /// lib.get(b"awesome_function\0").unwrap();
134 /// awesome_function(0.42);
135 /// }
136 /// ```
137 ///
138 /// A static variable may also be loaded and inspected:
139 ///
140 /// ```no_run
141 /// # use ::libloading::{Library, Symbol};
142 /// # let lib = unsafe { Library::new("/path/to/awesome.module").unwrap() };
143 /// unsafe {
144 /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
145 /// **awesome_variable = 42.0;
146 /// };
147 /// ```
148 pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, Error> {
149 self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
150 }
151
152 /// Unload the library.
153 ///
154 /// This method might be a no-op, depending on the flags with which the `Library` was opened,
155 /// what library was opened or other platform specifics.
156 ///
157 /// You only need to call this if you are interested in handling any errors that may arise when
158 /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
159 /// library and ignore the errors were they arise.
160 ///
161 /// The underlying data structures may still get leaked if an error does occur.
162 pub fn close(self) -> Result<(), Error> {
163 self.0.close()
164 }
165}
166
167impl fmt::Debug for Library {
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 self.0.fmt(f)
170 }
171}
172
173impl From<imp::Library> for Library {
174 fn from(lib: imp::Library) -> Library {
175 Library(lib)
176 }
177}
178
179impl From<Library> for imp::Library {
180 fn from(lib: Library) -> imp::Library {
181 lib.0
182 }
183}
184
185unsafe impl Send for Library {}
186unsafe impl Sync for Library {}
187
188/// Symbol from a library.
189///
190/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
191/// unloaded. The primary method to create an instance of a `Symbol` is via [`Library::get`].
192///
193/// The `Deref` trait implementation allows the use of `Symbol` as if it was a function or variable
194/// itself, without taking care to “extract” the function or variable manually most of the time.
195///
196/// [`Library::get`]: Library::get
197#[cfg_attr(libloading_docs, doc(cfg(any(unix, windows))))]
198pub struct Symbol<'lib, T: 'lib> {
199 inner: imp::Symbol<T>,
200 pd: marker::PhantomData<&'lib T>,
201}
202
203impl<'lib, T> Symbol<'lib, T> {
204 /// Extract the wrapped `os::platform::Symbol`.
205 ///
206 /// # Safety
207 ///
208 /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
209 /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
210 /// was loaded from.
211 ///
212 /// # Examples
213 ///
214 /// ```no_run
215 /// # use ::libloading::{Library, Symbol};
216 /// unsafe {
217 /// let lib = Library::new("/path/to/awesome.module").unwrap();
218 /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
219 /// let symbol = symbol.into_raw();
220 /// }
221 /// ```
222 pub unsafe fn into_raw(self) -> imp::Symbol<T> {
223 self.inner
224 }
225
226 /// Wrap the `os::platform::Symbol` into this safe wrapper.
227 ///
228 /// Note that, in order to create association between the symbol and the library this symbol
229 /// came from, this function requires a reference to the library.
230 ///
231 /// # Safety
232 ///
233 /// The `library` reference must be exactly the library `sym` was loaded from.
234 ///
235 /// # Examples
236 ///
237 /// ```no_run
238 /// # use ::libloading::{Library, Symbol};
239 /// unsafe {
240 /// let lib = Library::new("/path/to/awesome.module").unwrap();
241 /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
242 /// let symbol = symbol.into_raw();
243 /// let symbol = Symbol::from_raw(symbol, &lib);
244 /// }
245 /// ```
246 pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, library: &'lib L) -> Symbol<'lib, T> {
247 let _ = library; // ignore here for documentation purposes.
248 Symbol {
249 inner: sym,
250 pd: marker::PhantomData,
251 }
252 }
253
254 /// Try to convert the symbol into a raw pointer.
255 /// Success depends on the platform. Currently, this fn always succeeds and returns some.
256 ///
257 /// # Safety
258 ///
259 /// Using this function relinquishes all the lifetime guarantees. It is up to the developer to
260 /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
261 /// was loaded from.
262 pub unsafe fn try_as_raw_ptr(self) -> Option<*mut raw::c_void> {
263 Some(
264 #[allow(unused_unsafe)] // 1.56.0 compat
265 unsafe {
266 // SAFE: the calling function has the same soundness invariants as this callee.
267 self.into_raw()
268 }
269 .as_raw_ptr(),
270 )
271 }
272}
273
274impl<'lib, T> Symbol<'lib, Option<T>> {
275 /// Lift Option out of the symbol.
276 ///
277 /// # Examples
278 ///
279 /// ```no_run
280 /// # use ::libloading::{Library, Symbol};
281 /// unsafe {
282 /// let lib = Library::new("/path/to/awesome.module").unwrap();
283 /// let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap();
284 /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
285 /// }
286 /// ```
287 pub fn lift_option(self) -> Option<Symbol<'lib, T>> {
288 self.inner.lift_option().map(|is| Symbol {
289 inner: is,
290 pd: marker::PhantomData,
291 })
292 }
293}
294
295impl<'lib, T> Clone for Symbol<'lib, T> {
296 fn clone(&self) -> Symbol<'lib, T> {
297 Symbol {
298 inner: self.inner.clone(),
299 pd: marker::PhantomData,
300 }
301 }
302}
303
304// FIXME: implement FnOnce for callable stuff instead.
305impl<T> ops::Deref for Symbol<'_, T> {
306 type Target = T;
307 fn deref(&self) -> &T {
308 ops::Deref::deref(&self.inner)
309 }
310}
311
312impl<T> fmt::Debug for Symbol<'_, T> {
313 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
314 self.inner.fmt(f)
315 }
316}
317
318unsafe impl<T: Send> Send for Symbol<'_, T> {}
319unsafe impl<T: Sync> Sync for Symbol<'_, T> {}
320