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