1 | // A hack for docs.rs to build documentation that has both windows and linux documentation in the |
2 | // same rustdoc build visible. |
3 | #[cfg (all(libloading_docs, not(unix)))] |
4 | mod unix_imports {} |
5 | #[cfg (any(not(libloading_docs), unix))] |
6 | mod unix_imports { |
7 | pub(super) use std::os::unix::ffi::OsStrExt; |
8 | } |
9 | |
10 | pub use self::consts::*; |
11 | use self::unix_imports::*; |
12 | use std::ffi::{CStr, OsStr}; |
13 | use std::os::raw; |
14 | use std::{fmt, marker, mem, ptr}; |
15 | use util::{cstr_cow_from_bytes, ensure_compatible_types}; |
16 | |
17 | mod consts; |
18 | |
19 | // dl* family of functions did not have enough thought put into it. |
20 | // |
21 | // Whole error handling scheme is done via setting and querying some global state, therefore it is |
22 | // not safe to use dynamic library loading in MT-capable environment at all. Only in POSIX 2008+TC1 |
23 | // a thread-local state was allowed for `dlerror`, making the dl* family of functions MT-safe. |
24 | // |
25 | // In practice (as of 2020-04-01) most of the widely used targets use a thread-local for error |
26 | // state and have been doing so for a long time. Regardless the comments in this function shall |
27 | // remain as a documentation for the future generations. |
28 | fn with_dlerror<T, F>(wrap: fn(crate::error::DlDescription) -> crate::Error, closure: F) |
29 | -> Result<T, Option<crate::Error>> |
30 | where F: FnOnce() -> Option<T> { |
31 | // We used to guard all uses of dl* functions with our own mutex. This made them safe to use in |
32 | // MT programs provided the only way a program used dl* was via this library. However, it also |
33 | // had a number of downsides or cases where it failed to handle the problems. For instance, |
34 | // if any other library called `dlerror` internally concurrently with `libloading` things would |
35 | // still go awry. |
36 | // |
37 | // On platforms where `dlerror` is still MT-unsafe, `dlsym` (`Library::get`) can spuriously |
38 | // succeed and return a null pointer for a symbol when the actual symbol look-up operation |
39 | // fails. Instances where the actual symbol _could_ be `NULL` are platform specific. For |
40 | // instance on GNU glibc based-systems (an excerpt from dlsym(3)): |
41 | // |
42 | // > The value of a symbol returned by dlsym() will never be NULL if the shared object is the |
43 | // > result of normal compilation, since a global symbol is never placed at the NULL |
44 | // > address. There are nevertheless cases where a lookup using dlsym() may return NULL as the |
45 | // > value of a symbol. For example, the symbol value may be the result of a GNU indirect |
46 | // > function (IFUNC) resolver function that returns NULL as the resolved value. |
47 | |
48 | // While we could could call `dlerror` here to clear the previous error value, only the `dlsym` |
49 | // call depends on it being cleared beforehand and only in some cases too. We will instead |
50 | // clear the error inside the dlsym binding instead. |
51 | // |
52 | // In all the other cases, clearing the error here will only be hiding misuse of these bindings |
53 | // or a bug in implementation of dl* family of functions. |
54 | closure().ok_or_else(|| unsafe { |
55 | // This code will only get executed if the `closure` returns `None`. |
56 | let error = dlerror(); |
57 | if error.is_null() { |
58 | // In non-dlsym case this may happen when there’re bugs in our bindings or there’s |
59 | // non-libloading user of libdl; possibly in another thread. |
60 | None |
61 | } else { |
62 | // You can’t even rely on error string being static here; call to subsequent dlerror |
63 | // may invalidate or overwrite the error message. Why couldn’t they simply give up the |
64 | // ownership over the message? |
65 | // TODO: should do locale-aware conversion here. OTOH Rust doesn’t seem to work well in |
66 | // any system that uses non-utf8 locale, so I doubt there’s a problem here. |
67 | let message = CStr::from_ptr(error).into(); |
68 | Some(wrap(crate::error::DlDescription(message))) |
69 | // Since we do a copy of the error string above, maybe we should call dlerror again to |
70 | // let libdl know it may free its copy of the string now? |
71 | } |
72 | }) |
73 | } |
74 | |
75 | /// A platform-specific counterpart of the cross-platform [`Library`](crate::Library). |
76 | pub struct Library { |
77 | handle: *mut raw::c_void |
78 | } |
79 | |
80 | unsafe impl Send for Library {} |
81 | |
82 | // That being said... this section in the volume 2 of POSIX.1-2008 states: |
83 | // |
84 | // > All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the |
85 | // > following functions need not be thread-safe. |
86 | // |
87 | // With notable absence of any dl* function other than dlerror in the list. By “this volume” |
88 | // I suppose they refer precisely to the “volume 2”. dl* family of functions are specified |
89 | // by this same volume, so the conclusion is indeed that dl* functions are required by POSIX |
90 | // to be thread-safe. Great! |
91 | // |
92 | // See for more details: |
93 | // |
94 | // * https://github.com/nagisa/rust_libloading/pull/17 |
95 | // * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01 |
96 | unsafe impl Sync for Library {} |
97 | |
98 | impl Library { |
99 | /// Find and eagerly load a shared library (module). |
100 | /// |
101 | /// If the `filename` contains a [path separator], the `filename` is interpreted as a `path` to |
102 | /// a file. Otherwise, platform-specific algorithms are employed to find a library with a |
103 | /// matching file name. |
104 | /// |
105 | /// This is equivalent to <code>[Library::open](filename, [RTLD_LAZY] | [RTLD_LOCAL])</code>. |
106 | /// |
107 | /// [path separator]: std::path::MAIN_SEPARATOR |
108 | /// |
109 | /// # Safety |
110 | /// |
111 | /// When a library is loaded, initialisation routines contained within the library are executed. |
112 | /// For the purposes of safety, the execution of these routines is conceptually the same calling an |
113 | /// unknown foreign function and may impose arbitrary requirements on the caller for the call |
114 | /// to be sound. |
115 | /// |
116 | /// Additionally, the callers of this function must also ensure that execution of the |
117 | /// termination routines contained within the library is safe as well. These routines may be |
118 | /// executed when the library is unloaded. |
119 | #[inline ] |
120 | pub unsafe fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, crate::Error> { |
121 | Library::open(Some(filename), RTLD_LAZY | RTLD_LOCAL) |
122 | } |
123 | |
124 | /// Load the `Library` representing the current executable. |
125 | /// |
126 | /// [`Library::get`] calls of the returned `Library` will look for symbols in following |
127 | /// locations in order: |
128 | /// |
129 | /// 1. The original program image; |
130 | /// 2. Any executable object files (e.g. shared libraries) loaded at program startup; |
131 | /// 3. Any executable object files loaded at runtime (e.g. via other `Library::new` calls or via |
132 | /// calls to the `dlopen` function). |
133 | /// |
134 | /// Note that the behaviour of a `Library` loaded with this method is different from that of |
135 | /// Libraries loaded with [`os::windows::Library::this`]. |
136 | /// |
137 | /// This is equivalent to <code>[Library::open](None, [RTLD_LAZY] | [RTLD_LOCAL])</code>. |
138 | /// |
139 | /// [`os::windows::Library::this`]: crate::os::windows::Library::this |
140 | #[inline ] |
141 | pub fn this() -> Library { |
142 | unsafe { |
143 | // SAFE: this does not load any new shared library images, no danger in it executing |
144 | // initialiser routines. |
145 | Library::open(None::<&OsStr>, RTLD_LAZY | RTLD_LOCAL).expect("this should never fail" ) |
146 | } |
147 | } |
148 | |
149 | /// Find and load an executable object file (shared library). |
150 | /// |
151 | /// See documentation for [`Library::this`] for further description of the behaviour |
152 | /// when the `filename` is `None`. Otherwise see [`Library::new`]. |
153 | /// |
154 | /// Corresponds to `dlopen(filename, flags)`. |
155 | /// |
156 | /// # Safety |
157 | /// |
158 | /// When a library is loaded, initialisation routines contained within the library are executed. |
159 | /// For the purposes of safety, the execution of these routines is conceptually the same calling an |
160 | /// unknown foreign function and may impose arbitrary requirements on the caller for the call |
161 | /// to be sound. |
162 | /// |
163 | /// Additionally, the callers of this function must also ensure that execution of the |
164 | /// termination routines contained within the library is safe as well. These routines may be |
165 | /// executed when the library is unloaded. |
166 | pub unsafe fn open<P>(filename: Option<P>, flags: raw::c_int) -> Result<Library, crate::Error> |
167 | where P: AsRef<OsStr> { |
168 | let filename = match filename { |
169 | None => None, |
170 | Some(ref f) => Some(cstr_cow_from_bytes(f.as_ref().as_bytes())?), |
171 | }; |
172 | with_dlerror(|desc| crate::Error::DlOpen { desc }, move || { |
173 | let result = dlopen(match filename { |
174 | None => ptr::null(), |
175 | Some(ref f) => f.as_ptr() |
176 | }, flags); |
177 | // ensure filename lives until dlopen completes |
178 | drop(filename); |
179 | if result.is_null() { |
180 | None |
181 | } else { |
182 | Some(Library { |
183 | handle: result |
184 | }) |
185 | } |
186 | }).map_err(|e| e.unwrap_or(crate::Error::DlOpenUnknown)) |
187 | } |
188 | |
189 | unsafe fn get_impl<T, F>(&self, symbol: &[u8], on_null: F) -> Result<Symbol<T>, crate::Error> |
190 | where F: FnOnce() -> Result<Symbol<T>, crate::Error> |
191 | { |
192 | ensure_compatible_types::<T, *mut raw::c_void>()?; |
193 | let symbol = cstr_cow_from_bytes(symbol)?; |
194 | // `dlsym` may return nullptr in two cases: when a symbol genuinely points to a null |
195 | // pointer or the symbol cannot be found. In order to detect this case a double dlerror |
196 | // pattern must be used, which is, sadly, a little bit racy. |
197 | // |
198 | // We try to leave as little space as possible for this to occur, but we can’t exactly |
199 | // fully prevent it. |
200 | match with_dlerror(|desc| crate::Error::DlSym { desc }, || { |
201 | dlerror(); |
202 | let symbol = dlsym(self.handle, symbol.as_ptr()); |
203 | if symbol.is_null() { |
204 | None |
205 | } else { |
206 | Some(Symbol { |
207 | pointer: symbol, |
208 | pd: marker::PhantomData |
209 | }) |
210 | } |
211 | }) { |
212 | Err(None) => on_null(), |
213 | Err(Some(e)) => Err(e), |
214 | Ok(x) => Ok(x) |
215 | } |
216 | |
217 | } |
218 | |
219 | /// Get a pointer to a function or static variable by symbol name. |
220 | /// |
221 | /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a |
222 | /// null terminated `symbol` may help to avoid an allocation. |
223 | /// |
224 | /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are |
225 | /// most likely invalid. |
226 | /// |
227 | /// # Safety |
228 | /// |
229 | /// Users of this API must specify the correct type of the function or variable loaded. Using a |
230 | /// `Symbol` with a wrong type is undefined. |
231 | /// |
232 | /// # Platform-specific behaviour |
233 | /// |
234 | /// Implementation of thread local variables is extremely platform specific and uses of such |
235 | /// variables that work on e.g. Linux may have unintended behaviour on other targets. |
236 | /// |
237 | /// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such |
238 | /// as FreeBSD), this function will unconditionally return an error when the underlying `dlsym` |
239 | /// call returns a null pointer. There are rare situations where `dlsym` returns a genuine null |
240 | /// pointer without it being an error. If loading a null pointer is something you care about, |
241 | /// consider using the [`Library::get_singlethreaded`] call. |
242 | #[inline (always)] |
243 | pub unsafe fn get<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> { |
244 | extern crate cfg_if; |
245 | cfg_if::cfg_if! { |
246 | // These targets are known to have MT-safe `dlerror`. |
247 | if #[cfg(any( |
248 | target_os = "linux" , |
249 | target_os = "android" , |
250 | target_os = "openbsd" , |
251 | target_os = "macos" , |
252 | target_os = "ios" , |
253 | target_os = "solaris" , |
254 | target_os = "illumos" , |
255 | target_os = "redox" , |
256 | target_os = "fuchsia" |
257 | ))] { |
258 | self.get_singlethreaded(symbol) |
259 | } else { |
260 | self.get_impl(symbol, || Err(crate::Error::DlSymUnknown)) |
261 | } |
262 | } |
263 | } |
264 | |
265 | /// Get a pointer to function or static variable by symbol name. |
266 | /// |
267 | /// The `symbol` may not contain any null bytes, with the exception of the last byte. Providing a |
268 | /// null terminated `symbol` may help to avoid an allocation. |
269 | /// |
270 | /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are |
271 | /// most likely invalid. |
272 | /// |
273 | /// # Safety |
274 | /// |
275 | /// Users of this API must specify the correct type of the function or variable loaded. |
276 | /// |
277 | /// It is up to the user of this library to ensure that no other calls to an MT-unsafe |
278 | /// implementation of `dlerror` occur during the execution of this function. Failing that, the |
279 | /// behaviour of this function is not defined. |
280 | /// |
281 | /// # Platform-specific behaviour |
282 | /// |
283 | /// The implementation of thread-local variables is extremely platform specific and uses of such |
284 | /// variables that work on e.g. Linux may have unintended behaviour on other targets. |
285 | #[inline (always)] |
286 | pub unsafe fn get_singlethreaded<T>(&self, symbol: &[u8]) -> Result<Symbol<T>, crate::Error> { |
287 | self.get_impl(symbol, || Ok(Symbol { |
288 | pointer: ptr::null_mut(), |
289 | pd: marker::PhantomData |
290 | })) |
291 | } |
292 | |
293 | /// Convert the `Library` to a raw handle. |
294 | /// |
295 | /// The handle returned by this function shall be usable with APIs which accept handles |
296 | /// as returned by `dlopen`. |
297 | pub fn into_raw(self) -> *mut raw::c_void { |
298 | let handle = self.handle; |
299 | mem::forget(self); |
300 | handle |
301 | } |
302 | |
303 | /// Convert a raw handle returned by `dlopen`-family of calls to a `Library`. |
304 | /// |
305 | /// # Safety |
306 | /// |
307 | /// The pointer shall be a result of a successful call of the `dlopen`-family of functions or a |
308 | /// pointer previously returned by `Library::into_raw` call. It must be valid to call `dlclose` |
309 | /// with this pointer as an argument. |
310 | pub unsafe fn from_raw(handle: *mut raw::c_void) -> Library { |
311 | Library { |
312 | handle |
313 | } |
314 | } |
315 | |
316 | /// Unload the library. |
317 | /// |
318 | /// This method might be a no-op, depending on the flags with which the `Library` was opened, |
319 | /// what library was opened or other platform specifics. |
320 | /// |
321 | /// You only need to call this if you are interested in handling any errors that may arise when |
322 | /// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the |
323 | /// library and ignore the errors were they arise. |
324 | /// |
325 | /// The underlying data structures may still get leaked if an error does occur. |
326 | pub fn close(self) -> Result<(), crate::Error> { |
327 | let result = with_dlerror(|desc| crate::Error::DlClose { desc }, || { |
328 | if unsafe { dlclose(self.handle) } == 0 { |
329 | Some(()) |
330 | } else { |
331 | None |
332 | } |
333 | }).map_err(|e| e.unwrap_or(crate::Error::DlCloseUnknown)); |
334 | // While the library is not free'd yet in case of an error, there is no reason to try |
335 | // dropping it again, because all that will do is try calling `dlclose` again. only |
336 | // this time it would ignore the return result, which we already seen failing… |
337 | std::mem::forget(self); |
338 | result |
339 | } |
340 | } |
341 | |
342 | impl Drop for Library { |
343 | fn drop(&mut self) { |
344 | unsafe { |
345 | dlclose(self.handle); |
346 | } |
347 | } |
348 | } |
349 | |
350 | impl fmt::Debug for Library { |
351 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
352 | f.write_str(&format!("Library@ {:p}" , self.handle)) |
353 | } |
354 | } |
355 | |
356 | /// Symbol from a library. |
357 | /// |
358 | /// A major difference compared to the cross-platform `Symbol` is that this does not ensure that the |
359 | /// `Symbol` does not outlive the `Library` it comes from. |
360 | pub struct Symbol<T> { |
361 | pointer: *mut raw::c_void, |
362 | pd: marker::PhantomData<T> |
363 | } |
364 | |
365 | impl<T> Symbol<T> { |
366 | /// Convert the loaded `Symbol` into a raw pointer. |
367 | pub fn into_raw(self) -> *mut raw::c_void { |
368 | self.pointer |
369 | } |
370 | } |
371 | |
372 | impl<T> Symbol<Option<T>> { |
373 | /// Lift Option out of the symbol. |
374 | pub fn lift_option(self) -> Option<Symbol<T>> { |
375 | if self.pointer.is_null() { |
376 | None |
377 | } else { |
378 | Some(Symbol { |
379 | pointer: self.pointer, |
380 | pd: marker::PhantomData, |
381 | }) |
382 | } |
383 | } |
384 | } |
385 | |
386 | unsafe impl<T: Send> Send for Symbol<T> {} |
387 | unsafe impl<T: Sync> Sync for Symbol<T> {} |
388 | |
389 | impl<T> Clone for Symbol<T> { |
390 | fn clone(&self) -> Symbol<T> { |
391 | Symbol { ..*self } |
392 | } |
393 | } |
394 | |
395 | impl<T> ::std::ops::Deref for Symbol<T> { |
396 | type Target = T; |
397 | fn deref(&self) -> &T { |
398 | unsafe { |
399 | // Additional reference level for a dereference on `deref` return value. |
400 | &*(&self.pointer as *const *mut _ as *const T) |
401 | } |
402 | } |
403 | } |
404 | |
405 | impl<T> fmt::Debug for Symbol<T> { |
406 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
407 | unsafe { |
408 | let mut info: MaybeUninit = mem::MaybeUninit::<DlInfo>::uninit(); |
409 | if dladdr(self.pointer, info:info.as_mut_ptr()) != 0 { |
410 | let info: DlInfo = info.assume_init(); |
411 | if info.dli_sname.is_null() { |
412 | f.write_str(&format!("Symbol@ {:p} from {:?}" , |
413 | self.pointer, |
414 | CStr::from_ptr(info.dli_fname))) |
415 | } else { |
416 | f.write_str(&format!("Symbol {:?}@ {:p} from {:?}" , |
417 | CStr::from_ptr(info.dli_sname), self.pointer, |
418 | CStr::from_ptr(info.dli_fname))) |
419 | } |
420 | } else { |
421 | f.write_str(&format!("Symbol@ {:p}" , self.pointer)) |
422 | } |
423 | } |
424 | } |
425 | } |
426 | |
427 | // Platform specific things |
428 | #[cfg_attr (any(target_os = "linux" , target_os = "android" ), link(name="dl" ))] |
429 | #[cfg_attr (any(target_os = "freebsd" , target_os = "dragonfly" ), link(name="c" ))] |
430 | extern { |
431 | fn dlopen(filename: *const raw::c_char, flags: raw::c_int) -> *mut raw::c_void; |
432 | fn dlclose(handle: *mut raw::c_void) -> raw::c_int; |
433 | fn dlsym(handle: *mut raw::c_void, symbol: *const raw::c_char) -> *mut raw::c_void; |
434 | fn dlerror() -> *mut raw::c_char; |
435 | fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int; |
436 | } |
437 | |
438 | #[repr (C)] |
439 | struct DlInfo { |
440 | dli_fname: *const raw::c_char, |
441 | dli_fbase: *mut raw::c_void, |
442 | dli_sname: *const raw::c_char, |
443 | dli_saddr: *mut raw::c_void |
444 | } |
445 | |