blob: f6fc58d1ae7a3b1a976de65fcb676b9a8e6af754 [file] [log] [blame]
//! A memory-safer wrapper around system dynamic library loading primitives.
//!
//! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as
//! shared libraries) as well as use functions and static variables these libraries contain.
//!
//! While the library does expose a cross-platform interface to load a library and find stuff
//! inside it, little is done to paper over the platform differences, especially where library
//! loading is involved. The documentation for each function will attempt to document such
//! differences on the best-effort basis.
//!
//! Less safe, platform specific bindings are also available. See the
//! [`os::platform`](os/index.html) module for details.
//!
//! # Usage
//!
//! Add a dependency on this library to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! libloading = "0.6"
//! ```
//!
//! Then inside your project
//!
//! ```no_run
//! extern crate libloading as lib;
//!
//! fn call_dynamic() -> Result<u32, Box<dyn std::error::Error>> {
//! let lib = lib::Library::new("/path/to/liblibrary.so")?;
//! unsafe {
//! let func: lib::Symbol<unsafe extern fn() -> u32> = lib.get(b"my_func")?;
//! Ok(func())
//! }
//! }
//! ```
//!
//! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes
//! from, preventing a common cause of undefined behaviour and memory safety problems.
#![deny(
missing_docs,
clippy::all,
unreachable_pub,
unused,
)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::ops;
use std::marker;
#[cfg(unix)]
use self::os::unix as imp;
#[cfg(windows)]
use self::os::windows as imp;
pub use self::error::Error;
pub mod os;
pub mod changelog;
mod util;
mod error;
/// A loaded dynamic library.
pub struct Library(imp::Library);
impl Library {
/// Find and load a dynamic library.
///
/// The `filename` argument may be any of:
///
/// * A library filename;
/// * Absolute path to the library;
/// * Relative (to the current working directory) path to the library.
///
/// # Thread-safety
///
/// The implementation strives to be as MT-safe as sanely possible, however due to certain
/// error-handling related resources not always being safe, this library is not MT-safe either.
///
/// * On Windows Vista and earlier error handling falls back to [`SetErrorMode`], which is not
/// MT-safe. MT-scenarios involving this function may cause a traditional data race;
/// * On some UNIX targets `dlerror` might not be MT-safe, resulting in garbage error messages
/// in certain MT-scenarios.
///
/// [`SetErrorMode`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx
///
/// Calling this function from multiple threads is not safe if used in conjunction with
/// relative filenames and the library search path is modified (`SetDllDirectory` function on
/// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX).
///
/// # Platform-specific behaviour
///
/// When a plain library filename is supplied, locations where library is searched for is
/// platform specific and cannot be adjusted in a portable manner. See documentation for
/// the platform specific [`os::unix::Library::new`] and [`os::windows::Library::new`] methods
/// for further information on library lookup behaviour.
///
/// ## Windows
///
/// If the `filename` specifies a library filename without path and with extension omitted,
/// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a
/// trailing `.` to the `filename`.
///
/// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s
/// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows
/// Vista.
///
/// # Tips
///
/// Distributing your dynamic libraries under a filename common to all platforms (e.g.
/// `awesome.module`) allows to avoid code which has to account for platform’s conventional
/// library filenames.
///
/// Strive to specify an absolute or at least a relative path to your library, unless
/// system-wide libraries are being loaded. Platform-dependent library search locations
/// combined with various quirks related to path-less filenames may cause flakiness in
/// programs.
///
/// # Examples
///
/// ```no_run
/// # use ::libloading::Library;
/// // Any of the following are valid.
/// let _ = Library::new("/path/to/awesome.module").unwrap();
/// let _ = Library::new("../awesome.module").unwrap();
/// let _ = Library::new("libsomelib.so.1").unwrap();
/// ```
pub fn new<P: AsRef<OsStr>>(filename: P) -> Result<Library, Error> {
imp::Library::new(filename).map(From::from)
}
/// Get a pointer to function or static variable by symbol name.
///
/// The `symbol` may not contain any null bytes, with an exception of last byte. A null
/// terminated `symbol` may avoid a string allocation in some cases.
///
/// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are
/// most likely invalid.
///
/// # Safety
///
/// Pointer to a value of arbitrary type is returned. Using a value with wrong type is
/// undefined.
///
/// # Platform-specific behaviour
///
/// Implementation of thread local variables is extremely platform specific and uses of these
/// variables that work on e.g. Linux may have unintended behaviour on other POSIX systems or
/// Windows.
///
/// On POSIX implementations where the `dlerror` function is not confirmed to be MT-safe (such
/// as FreeBSD), this function will unconditionally return an error the underlying `dlsym` call
/// returns a null pointer. There are rare situations where `dlsym` returns a genuine null
/// pointer without it being an error. If loading a null pointer is something you care about,
/// consider using the [`os::unix::Library::get_singlethreaded`] call.
///
/// # Examples
///
/// Given a loaded library:
///
/// ```no_run
/// # use ::libloading::Library;
/// let lib = Library::new("/path/to/awesome.module").unwrap();
/// ```
///
/// Loading and using a function looks like this:
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
/// # let lib = Library::new("/path/to/awesome.module").unwrap();
/// unsafe {
/// let awesome_function: Symbol<unsafe extern fn(f64) -> f64> =
/// lib.get(b"awesome_function\0").unwrap();
/// awesome_function(0.42);
/// }
/// ```
///
/// A static variable may also be loaded and inspected:
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
/// # let lib = Library::new("/path/to/awesome.module").unwrap();
/// unsafe {
/// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap();
/// **awesome_variable = 42.0;
/// };
/// ```
pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result<Symbol<'lib, T>, Error> {
self.0.get(symbol).map(|from| Symbol::from_raw(from, self))
}
/// Unload the library.
///
/// This method might be a no-op, depending on the flags with which the `Library` was opened,
/// what library was opened or other platform specifics.
///
/// You only need to call this if you are interested in handling any errors that may arise when
/// library is unloaded. Otherwise the implementation of `Drop` for `Library` will close the
/// library and ignore the errors were they arise.
pub fn close(self) -> Result<(), Error> {
self.0.close()
}
}
impl fmt::Debug for Library {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<imp::Library> for Library {
fn from(lib: imp::Library) -> Library {
Library(lib)
}
}
impl From<Library> for imp::Library {
fn from(lib: Library) -> imp::Library {
lib.0
}
}
unsafe impl Send for Library {}
unsafe impl Sync for Library {}
/// Symbol from a library.
///
/// This type is a safeguard against using dynamically loaded symbols after a `Library` is
/// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`.
///
/// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a
/// function or variable directly, without taking care to “extract” function or variable manually
/// most of the time.
///
/// See [`Library::get`] for details.
///
/// [`Library::get`]: ./struct.Library.html#method.get
pub struct Symbol<'lib, T: 'lib> {
inner: imp::Symbol<T>,
pd: marker::PhantomData<&'lib T>
}
impl<'lib, T> Symbol<'lib, T> {
/// Extract the wrapped `os::platform::Symbol`.
///
/// # Safety
///
/// Using this function relinquishes all the lifetime guarantees. It is up to programmer to
/// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol
/// was loaded from.
///
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
/// let lib = Library::new("/path/to/awesome.module").unwrap();
/// unsafe {
/// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
/// let symbol = symbol.into_raw();
/// }
/// ```
pub unsafe fn into_raw(self) -> imp::Symbol<T> {
self.inner
}
/// Wrap the `os::platform::Symbol` into this safe wrapper.
///
/// Note that, in order to create association between the symbol and the library this symbol
/// came from, this function requires reference to the library provided.
///
/// # Safety
///
/// It is invalid to provide a reference to any other value other than the library the `sym`
/// was loaded from. Doing so invalidates any lifetime guarantees.
///
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
/// let lib = Library::new("/path/to/awesome.module").unwrap();
/// unsafe {
/// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap();
/// let symbol = symbol.into_raw();
/// let symbol = Symbol::from_raw(symbol, &lib);
/// }
/// ```
pub unsafe fn from_raw<L>(sym: imp::Symbol<T>, _: &'lib L) -> Symbol<'lib, T> {
Symbol {
inner: sym,
pd: marker::PhantomData
}
}
}
impl<'lib, T> Symbol<'lib, Option<T>> {
/// Lift Option out of the symbol.
///
/// # Examples
///
/// ```no_run
/// # use ::libloading::{Library, Symbol};
/// let lib = Library::new("/path/to/awesome.module").unwrap();
/// unsafe {
/// let symbol: Symbol<Option<*mut u32>> = lib.get(b"symbol\0").unwrap();
/// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null");
/// }
/// ```
pub fn lift_option(self) -> Option<Symbol<'lib, T>> {
self.inner.lift_option().map(|is| Symbol {
inner: is,
pd: marker::PhantomData,
})
}
}
impl<'lib, T> Clone for Symbol<'lib, T> {
fn clone(&self) -> Symbol<'lib, T> {
Symbol {
inner: self.inner.clone(),
pd: marker::PhantomData
}
}
}
// FIXME: implement FnOnce for callable stuff instead.
impl<'lib, T> ops::Deref for Symbol<'lib, T> {
type Target = T;
fn deref(&self) -> &T {
ops::Deref::deref(&self.inner)
}
}
impl<'lib, T> fmt::Debug for Symbol<'lib, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.inner.fmt(f)
}
}
unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {}
unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {}
/// Converts a library name to a filename generally appropriate for use on the system.
///
/// The function will prepend prefixes (such as `lib`) and suffixes (such as `.so`) to the library
/// `name` to construct the filename.
///
/// # Examples
///
/// It can be used to load global libraries in a platform independent manner:
///
/// ```
/// use libloading::{Library, library_filename};
/// // Will attempt to load `libLLVM.so` on Linux, `libLLVM.dylib` on macOS and `LLVM.dll` on
/// // Windows.
/// let library = Library::new(library_filename("LLVM"));
/// ```
pub fn library_filename<S: AsRef<OsStr>>(name: S) -> OsString {
let name = name.as_ref();
let mut string = OsString::with_capacity(name.len() + DLL_PREFIX.len() + DLL_SUFFIX.len());
string.push(DLL_PREFIX);
string.push(name);
string.push(DLL_SUFFIX);
string
}