├── .gitignore ├── tests ├── nagisa32.dll ├── nagisa64.dll ├── windows.rs ├── markers.rs └── functions.rs ├── .travis.yml ├── src ├── os │ ├── unix │ │ ├── global_static.c │ │ └── mod.rs │ ├── mod.rs │ └── windows │ │ └── mod.rs ├── test_helpers.rs ├── util.rs ├── changelog.rs └── lib.rs ├── Cargo.toml ├── appveyor.yml ├── LICENSE └── README.mkd /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | doc 4 | -------------------------------------------------------------------------------- /tests/nagisa32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pycckue-Bnepeg/rust_libloading/master/tests/nagisa32.dll -------------------------------------------------------------------------------- /tests/nagisa64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pycckue-Bnepeg/rust_libloading/master/tests/nagisa64.dll -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: false 3 | cache: cargo 4 | rust: 5 | - 1.14.0 6 | - stable 7 | - nightly 8 | os: 9 | - linux 10 | - osx 11 | env: 12 | - ARCH=x86_64 13 | - ARCH=i686 14 | notifications: 15 | email: false 16 | -------------------------------------------------------------------------------- /src/os/unix/global_static.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | pthread_mutex_t __attribute__((weak)) rust_libloading_dlerror_mutex = PTHREAD_MUTEX_INITIALIZER; 5 | 6 | void __attribute__((weak)) 7 | rust_libloading_dlerror_mutex_lock(void) 8 | { 9 | if (pthread_mutex_lock(&rust_libloading_dlerror_mutex) != 0) { 10 | abort(); 11 | } 12 | } 13 | 14 | void __attribute__((weak)) 15 | rust_libloading_dlerror_mutex_unlock(void) 16 | { 17 | if (pthread_mutex_unlock(&rust_libloading_dlerror_mutex) != 0) { 18 | abort(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libloading" 3 | version = "0.5.0" 4 | authors = ["Simonas Kazlauskas "] 5 | build = "build.rs" 6 | description = "A safer binding to platform’s dynamic library loading utilities" 7 | keywords = ["dlopen", "load", "shared", "dylib"] 8 | license = "ISC" 9 | repository = "https://github.com/nagisa/rust_libloading/" 10 | documentation = "https://docs.rs/libloading/" 11 | 12 | [build-dependencies.cc] 13 | version = "1.0" 14 | 15 | [target.'cfg(windows)'.dependencies.winapi] 16 | version = "0.3" 17 | features = [ 18 | "winerror", 19 | "errhandlingapi", 20 | "libloaderapi", 21 | ] 22 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - TARGET: nightly-x86_64-pc-windows-msvc 4 | - TARGET: nightly-i686-pc-windows-msvc 5 | - TARGET: nightly-x86_64-pc-windows-gnu 6 | - TARGET: nightly-i686-pc-windows-gnu 7 | - TARGET: 1.14.0-x86_64-pc-windows-msvc 8 | - TARGET: 1.14.0-i686-pc-windows-msvc 9 | - TARGET: 1.14.0-x86_64-pc-windows-gnu 10 | - TARGET: 1.14.0-i686-pc-windows-gnu 11 | install: 12 | - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" -FileName "rust.exe" 13 | - ps: .\rust.exe /VERYSILENT /NORESTART /DIR="C:\rust" | Out-Null 14 | - ps: $env:PATH="$env:PATH;C:\rust\bin" 15 | - rustc -vV 16 | - cargo -vV 17 | build: off 18 | test_script: 19 | - cargo test 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2015, Simonas Kazlauskas 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without 4 | fee is hereby granted, provided that the above copyright notice and this permission notice appear 5 | in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 8 | SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 9 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 11 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 12 | THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | # libloading [![Travis CI][tcii]][tci] [![Appveyor CI][acii]][aci] 2 | 3 | [tcii]: https://travis-ci.org/nagisa/rust_libloading.svg?branch=master 4 | [tci]: https://travis-ci.org/nagisa/rust_libloading 5 | [acii]: https://ci.appveyor.com/api/projects/status/cnncnu58qcxb1ikf/branch/master?svg=true 6 | [aci]: https://ci.appveyor.com/project/nagisa/rust-libloading 7 | 8 | A memory-safer wrapper around system dynamic library loading primitives. The most important safety 9 | guarantee by this library is prevention of dangling-`Symbol`s that may occur after a `Library` is 10 | unloaded. 11 | 12 | Using this library allows loading dynamic libraries (also known as shared libraries) as well as use 13 | functions and static variables these libraries contain. 14 | 15 | * [Documentation][docs] 16 | * [Changelog][changelog] 17 | 18 | [docs]: https://docs.rs/libloading/ 19 | [changelog]: https://docs.rs/libloading/*/libloading/changelog/index.html 20 | 21 | libloading is distributed under ISC (MIT-like) license. 22 | -------------------------------------------------------------------------------- /src/test_helpers.rs: -------------------------------------------------------------------------------- 1 | //! This is a separate file containing helpers for tests of this library. It is built into a 2 | //! dynamic library by the build.rs script. 3 | #![crate_type="dylib"] // FIXME: should become a cdylib in due time 4 | #![cfg_attr(test_nightly, feature(thread_local))] 5 | 6 | #[no_mangle] 7 | pub static mut TEST_STATIC_U32: u32 = 0; 8 | 9 | #[no_mangle] 10 | pub static mut TEST_STATIC_PTR: *mut () = 0 as *mut _; 11 | 12 | #[cfg(test_nightly)] 13 | #[thread_local] 14 | #[no_mangle] 15 | pub static mut TEST_THREAD_LOCAL: u32 = 0; 16 | 17 | #[no_mangle] 18 | pub extern "C" fn test_identity_u32(x: u32) -> u32 { 19 | x 20 | } 21 | 22 | #[repr(C)] 23 | pub struct S { 24 | a: u64, 25 | b: u32, 26 | c: u16, 27 | d: u8 28 | } 29 | 30 | #[no_mangle] 31 | pub extern "C" fn test_identity_struct(x: S) -> S { 32 | x 33 | } 34 | 35 | #[no_mangle] 36 | pub unsafe extern "C" fn test_get_static_u32() -> u32 { 37 | TEST_STATIC_U32 38 | } 39 | 40 | #[no_mangle] 41 | pub unsafe extern "C" fn test_check_static_ptr() -> bool { 42 | TEST_STATIC_PTR == (&mut TEST_STATIC_PTR as *mut *mut _ as *mut _) 43 | } 44 | 45 | #[cfg(test_nightly)] 46 | #[no_mangle] 47 | pub unsafe extern "C" fn test_get_thread_local() -> u32 { 48 | TEST_THREAD_LOCAL 49 | } 50 | -------------------------------------------------------------------------------- /src/os/mod.rs: -------------------------------------------------------------------------------- 1 | //! Unsafe, platform specific bindings to dynamic library loading facilities. 2 | //! 3 | //! These modules expose more extensive, powerful, less principled bindings to the dynamic 4 | //! library loading facilities. Use of these bindings come at the cost of less (in most cases, 5 | //! none at all) safety guarantees, which are provided by the top-level bindings. 6 | //! 7 | //! # Examples 8 | //! 9 | //! Using these modules will likely involve conditional compilation: 10 | //! 11 | //! ```ignore 12 | //! # extern crate libloading; 13 | //! #[cfg(unix)] 14 | //! use libloading::os::unix::*; 15 | //! #[cfg(windows)] 16 | //! use libloading::os::windows::*; 17 | //! ``` 18 | 19 | macro_rules! unix { 20 | ($item: item) => { 21 | /// UNIX implementation of dynamic library loading. 22 | /// 23 | /// This module should be expanded with more UNIX-specific functionality in the future. 24 | $item 25 | } 26 | } 27 | 28 | macro_rules! windows { 29 | ($item: item) => { 30 | /// Windows implementation of dynamic library loading. 31 | /// 32 | /// This module should be expanded with more Windows-specific functionality in the future. 33 | $item 34 | } 35 | } 36 | 37 | #[cfg(unix)] 38 | unix!(pub mod unix;); 39 | #[cfg(unix)] 40 | windows!(pub mod windows {}); 41 | 42 | #[cfg(windows)] 43 | windows!(pub mod windows;); 44 | #[cfg(windows)] 45 | unix!(pub mod unix {}); 46 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString, NulError, FromBytesWithNulError}; 2 | use std::borrow::Cow; 3 | use std::os::raw; 4 | 5 | #[derive(Debug)] 6 | pub struct NullError; 7 | 8 | impl From for NullError { 9 | fn from(_: NulError) -> NullError { 10 | NullError 11 | } 12 | } 13 | 14 | impl From for NullError { 15 | fn from(_: FromBytesWithNulError) -> NullError { 16 | NullError 17 | } 18 | } 19 | 20 | impl From for ::std::io::Error { 21 | fn from(e: NullError) -> ::std::io::Error { 22 | ::std::io::Error::new(::std::io::ErrorKind::Other, format!("{}", e)) 23 | } 24 | } 25 | 26 | impl ::std::error::Error for NullError { 27 | fn description(&self) -> &str { "non-final null byte found" } 28 | } 29 | 30 | impl ::std::fmt::Display for NullError { 31 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 32 | write!(f, "non-final null byte found") 33 | } 34 | } 35 | 36 | /// Checks for last byte and avoids allocating if it is zero. 37 | /// 38 | /// Non-last null bytes still result in an error. 39 | pub fn cstr_cow_from_bytes<'a>(slice: &'a [u8]) -> Result, NullError> { 40 | static ZERO: raw::c_char = 0; 41 | Ok(match slice.last() { 42 | // Slice out of 0 elements 43 | None => unsafe { Cow::Borrowed(CStr::from_ptr(&ZERO)) }, 44 | // Slice with trailing 0 45 | Some(&0) => Cow::Borrowed(try!(CStr::from_bytes_with_nul(slice))), 46 | // Slice with no trailing 0 47 | Some(_) => Cow::Owned(try!(CString::new(slice))), 48 | }) 49 | } 50 | 51 | #[inline] 52 | pub fn ensure_compatible_types() { 53 | #[cold] 54 | #[inline(never)] 55 | fn dopanic() { 56 | panic!("value of requested type cannot be dynamically loaded"); 57 | } 58 | if ::std::mem::size_of::() != ::std::mem::size_of::() { 59 | dopanic() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/windows.rs: -------------------------------------------------------------------------------- 1 | #![cfg(windows)] 2 | extern crate libloading; 3 | use libloading::os::windows::*; 4 | use std::ffi::CStr; 5 | 6 | // The ordinal DLL contains exactly one function (other than DllMain, that is) with ordinal number 7 | // 1. This function has the sugnature `fn() -> *const c_char` and returns a string "bunny\0" (in 8 | // reference to WindowsBunny). 9 | // 10 | // Both x86_64 and x86 versions of the .dll are functionally the same. Ideally we would compile the 11 | // dlls with well known ordinals from our own testing helpers library, but rustc does not allow 12 | // specifying a custom .def file (https://github.com/rust-lang/rust/issues/35089) 13 | // 14 | // The DLLs were kindly compiled by WindowsBunny (aka. @retep998). 15 | 16 | #[cfg(target_arch="x86")] 17 | fn load_ordinal_lib() -> Library { 18 | Library::new("tests/nagisa32.dll").expect("nagisa32.dll") 19 | } 20 | 21 | #[cfg(target_arch="x86_64")] 22 | fn load_ordinal_lib() -> Library { 23 | Library::new("tests/nagisa64.dll").expect("nagisa64.dll") 24 | } 25 | 26 | #[cfg(any(target_arch="x86", target_arch="x86_64"))] 27 | #[test] 28 | fn test_ordinal() { 29 | let lib = load_ordinal_lib(); 30 | unsafe { 31 | let windows: Symbol *const i8> = lib.get_ordinal(1).expect("function"); 32 | assert_eq!(CStr::from_ptr(windows()).to_bytes(), b"bunny"); 33 | } 34 | } 35 | 36 | #[cfg(any(target_arch="x86", target_arch="x86_64"))] 37 | #[test] 38 | fn test_ordinal_missing_fails() { 39 | let lib = load_ordinal_lib(); 40 | unsafe { 41 | let r: Result *const i8>, _> = lib.get_ordinal(2); 42 | r.err().unwrap(); 43 | let r: Result *const i8>, _> = lib.get_ordinal(!0); 44 | r.err().unwrap(); 45 | } 46 | } 47 | 48 | #[test] 49 | fn test_new_kernel23() { 50 | Library::new("kernel23").err().unwrap(); 51 | } 52 | 53 | #[test] 54 | fn test_new_kernel32_no_ext() { 55 | Library::new("kernel32").unwrap(); 56 | } 57 | -------------------------------------------------------------------------------- /tests/markers.rs: -------------------------------------------------------------------------------- 1 | extern crate libloading; 2 | 3 | #[cfg(test)] 4 | fn assert_send() {} 5 | #[cfg(test)] 6 | fn assert_sync() {} 7 | 8 | #[test] 9 | fn check_library_send() { 10 | assert_send::(); 11 | } 12 | 13 | #[cfg(unix)] 14 | #[test] 15 | fn check_unix_library_send() { 16 | assert_send::(); 17 | } 18 | 19 | #[cfg(windows)] 20 | #[test] 21 | fn check_windows_library_send() { 22 | assert_send::(); 23 | } 24 | 25 | #[test] 26 | fn check_library_sync() { 27 | assert_sync::(); 28 | } 29 | 30 | #[cfg(unix)] 31 | #[test] 32 | fn check_unix_library_sync() { 33 | assert_sync::(); 34 | } 35 | 36 | #[cfg(windows)] 37 | #[test] 38 | fn check_windows_library_sync() { 39 | assert_sync::(); 40 | } 41 | 42 | #[test] 43 | fn check_symbol_send() { 44 | assert_send:: ()>>(); 45 | // assert_not_send::>(); 46 | } 47 | 48 | #[cfg(unix)] 49 | #[test] 50 | fn check_unix_symbol_send() { 51 | assert_send:: ()>>(); 52 | // assert_not_send::>(); 53 | } 54 | 55 | #[cfg(windows)] 56 | #[test] 57 | fn check_windows_symbol_send() { 58 | assert_send:: ()>>(); 59 | } 60 | 61 | #[test] 62 | fn check_symbol_sync() { 63 | assert_sync:: ()>>(); 64 | // assert_not_sync::>(); 65 | } 66 | 67 | #[cfg(unix)] 68 | #[test] 69 | fn check_unix_symbol_sync() { 70 | assert_sync:: ()>>(); 71 | // assert_not_sync::>(); 72 | } 73 | 74 | #[cfg(windows)] 75 | #[test] 76 | fn check_windows_symbol_sync() { 77 | assert_sync:: ()>>(); 78 | // assert_not_sync::>(); 79 | } 80 | -------------------------------------------------------------------------------- /src/changelog.rs: -------------------------------------------------------------------------------- 1 | //! Project changelog 2 | 3 | /// Release 0.5.0 (2018-01-11) 4 | /// 5 | /// * Update to `winapi = ^0.3`; 6 | /// 7 | /// ## Breaking changes 8 | /// 9 | /// * libloading now requires a C compiler to build on UNIX; 10 | /// * This is a temporary measure until the [`linkage`] attribute is stabilised; 11 | /// * Necessary to resolve [#32]. 12 | /// 13 | /// [`linkage`]: https://github.com/rust-lang/rust/issues/29603 14 | /// [#32]: https://github.com/nagisa/rust_libloading/issues/32 15 | pub mod r0_5_0 {} 16 | 17 | /// Release 0.4.3 (2017-12-07) 18 | /// 19 | /// * Bump lazy-static dependency to `^1.0`; 20 | /// * `cargo test --release` now works when testing libloading. 21 | pub mod r0_4_3 {} 22 | 23 | 24 | /// Release 0.4.2 (2017-09-24) 25 | /// 26 | /// * Improved error and race-condition handling on Windows; 27 | /// * Improved documentation about thread-safety of Library; 28 | /// * Added `Symbol::::lift_option() -> Option>` convenience method. 29 | pub mod r0_4_2 {} 30 | 31 | 32 | /// Release 0.4.1 (2017-08-29) 33 | /// 34 | /// * Solaris support 35 | pub mod r0_4_1 {} 36 | 37 | /// Release 0.4.0 (2017-05-01) 38 | /// 39 | /// * Remove build-time dependency on target_build_utils (and by extension serde/phf); 40 | /// * Require at least version 1.14.0 of rustc to build; 41 | /// * Actually, it is cargo which has to be more recent here. The one shipped with rustc 1.14.0 42 | /// is what’s being required from now on. 43 | pub mod r0_4_0 {} 44 | 45 | /// Release 0.3.4 (2017-03-25) 46 | /// 47 | /// * Remove rogue println! 48 | pub mod r0_3_4 {} 49 | 50 | /// Release 0.3.3 (2017-03-25) 51 | /// 52 | /// * Panics when `Library::get` is called for incompatibly sized type such as named function 53 | /// types (which are zero-sized). 54 | pub mod r0_3_3 {} 55 | 56 | /// Release 0.3.2 (2017-02-10) 57 | /// 58 | /// * Minimum version required is now rustc 1.12.0; 59 | /// * Updated dependency versions (most notably target_build_utils to 0.3.0) 60 | pub mod r0_3_2 {} 61 | 62 | /// Release 0.3.1 (2016-10-01) 63 | /// 64 | /// * `Symbol` and `os::*::Symbol` now implement `Send` where `T: Send`; 65 | /// * `Symbol` and `os::*::Symbol` now implement `Sync` where `T: Sync`; 66 | /// * `Library` and `os::*::Library` now implement `Sync` (they were `Send` in 0.3.0 already). 67 | pub mod r0_3_1 {} 68 | 69 | /// Release 0.3.0 (2016-07-27) 70 | /// 71 | /// * Greatly improved documentation, especially around platform-specific behaviours; 72 | /// * Improved test suite by building our own library to test against; 73 | /// * All `Library`-ies now implement `Send`. 74 | /// * Added `impl From for Library` and `impl From for 75 | /// os::platform::Library` allowing wrapping and extracting the platform-specific library handle; 76 | /// * Added methods to wrap (`Symbol::from_raw`) and unwrap (`Symbol::into_raw`) the safe `Symbol` 77 | /// wrapper into unsafe `os::platform::Symbol`. 78 | /// 79 | /// The last two additions focus on not restricting potential usecases of this library, allowing 80 | /// users of the library to circumvent safety checks if need be. 81 | /// 82 | /// ## Breaking Changes 83 | /// 84 | /// `Library::new` defaults to `RTLD_NOW` instead of `RTLD_LAZY` on UNIX for more consistent 85 | /// cross-platform behaviour. If a library loaded with `Library::new` had any linking errors, but 86 | /// unresolved references weren’t forced to be resolved, the library would’ve “just worked”, 87 | /// whereas now the call to `Library::new` will return an error signifying presence of such error. 88 | /// 89 | /// ## os::platform 90 | /// * Added `os::unix::Library::open` which allows specifying arbitrary flags (e.g. `RTLD_LAZY`); 91 | /// * Added `os::windows::Library::get_ordinal` which allows finding a function or variable by its 92 | /// ordinal number; 93 | pub mod r0_3_0 {} 94 | -------------------------------------------------------------------------------- /tests/functions.rs: -------------------------------------------------------------------------------- 1 | extern crate libloading; 2 | use libloading::{Symbol, Library}; 3 | 4 | const LIBPATH: &'static str = concat!(env!("OUT_DIR"), "/libtest_helpers.dll"); 5 | 6 | fn make_helpers() { 7 | static ONCE: ::std::sync::Once = ::std::sync::ONCE_INIT; 8 | ONCE.call_once(|| { 9 | let mut outpath = String::from(if let Some(od) = option_env!("OUT_DIR") { od } else { return }); 10 | let rustc = option_env!("RUSTC").unwrap_or_else(|| { "rustc".into() }); 11 | outpath.push_str(&"/libtest_helpers.dll"); // extension for windows required, POSIX does not care. 12 | let _ = ::std::process::Command::new(rustc) 13 | .arg("src/test_helpers.rs") 14 | .arg("-o") 15 | .arg(outpath) 16 | .arg("-O") 17 | .output() 18 | .expect("could not compile the test helpers!"); 19 | }); 20 | } 21 | 22 | #[test] 23 | fn test_id_u32() { 24 | make_helpers(); 25 | let lib = Library::new(LIBPATH).unwrap(); 26 | unsafe { 27 | let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); 28 | assert_eq!(42, f(42)); 29 | } 30 | } 31 | 32 | #[repr(C)] 33 | #[derive(Clone,Copy,PartialEq,Debug)] 34 | struct S { 35 | a: u64, 36 | b: u32, 37 | c: u16, 38 | d: u8 39 | } 40 | 41 | #[test] 42 | fn test_id_struct() { 43 | make_helpers(); 44 | let lib = Library::new(LIBPATH).unwrap(); 45 | unsafe { 46 | let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); 47 | assert_eq!(S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 })); 48 | } 49 | } 50 | 51 | #[test] 52 | fn test_0_no_0() { 53 | make_helpers(); 54 | let lib = Library::new(LIBPATH).unwrap(); 55 | unsafe { 56 | let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); 57 | let f2: Symbol S> = lib.get(b"test_identity_struct").unwrap(); 58 | assert_eq!(*f, *f2); 59 | } 60 | } 61 | 62 | #[test] 63 | fn wrong_name_fails() { 64 | Library::new(concat!(env!("OUT_DIR"), "/libtest_help")).err().unwrap(); 65 | } 66 | 67 | #[test] 68 | fn missing_symbol_fails() { 69 | make_helpers(); 70 | let lib = Library::new(LIBPATH).unwrap(); 71 | unsafe { 72 | lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); 73 | lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); 74 | } 75 | } 76 | 77 | #[test] 78 | fn interior_null_fails() { 79 | make_helpers(); 80 | let lib = Library::new(LIBPATH).unwrap(); 81 | unsafe { 82 | lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); 83 | lib.get::<*mut ()>(b"test\0_does_not_exist\0").err().unwrap(); 84 | } 85 | } 86 | 87 | #[test] 88 | #[should_panic] 89 | fn test_incompatible_type() { 90 | make_helpers(); 91 | let lib = Library::new(LIBPATH).unwrap(); 92 | unsafe { 93 | let _ = lib.get::<()>(b"test_identity_u32\0"); 94 | } 95 | } 96 | 97 | #[test] 98 | #[should_panic] 99 | fn test_incompatible_type_named_fn() { 100 | make_helpers(); 101 | unsafe fn get<'a, T>(l: &'a Library, _: T) -> libloading::Result> { 102 | l.get::(b"test_identity_u32\0") 103 | } 104 | let lib = Library::new(LIBPATH).unwrap(); 105 | unsafe { 106 | let _ = get(&lib, test_incompatible_type_named_fn); 107 | } 108 | } 109 | 110 | #[test] 111 | fn test_static_u32() { 112 | make_helpers(); 113 | let lib = Library::new(LIBPATH).unwrap(); 114 | unsafe { 115 | let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); 116 | **var = 42; 117 | let help: Symbol u32> = lib.get(b"test_get_static_u32\0").unwrap(); 118 | assert_eq!(42, help()); 119 | } 120 | } 121 | 122 | #[test] 123 | fn test_static_ptr() { 124 | make_helpers(); 125 | let lib = Library::new(LIBPATH).unwrap(); 126 | unsafe { 127 | let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); 128 | **var = *var as *mut _; 129 | let works: Symbol bool> = 130 | lib.get(b"test_check_static_ptr\0").unwrap(); 131 | assert!(works()); 132 | } 133 | } 134 | 135 | #[cfg(any(windows, target_os="linux"))] 136 | #[cfg(test_nightly)] 137 | #[test] 138 | fn test_tls_static() { 139 | make_helpers(); 140 | let lib = Library::new(LIBPATH).unwrap(); 141 | unsafe { 142 | let var: Symbol<*mut u32> = lib.get(b"TEST_THREAD_LOCAL\0").unwrap(); 143 | **var = 84; 144 | let help: Symbol u32> = lib.get(b"test_get_thread_local\0").unwrap(); 145 | assert_eq!(84, help()); 146 | } 147 | ::std::thread::spawn(move || unsafe { 148 | let help: Symbol u32> = lib.get(b"test_get_thread_local\0").unwrap(); 149 | assert_eq!(0, help()); 150 | }).join().unwrap(); 151 | } 152 | -------------------------------------------------------------------------------- /src/os/windows/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate winapi; 2 | use self::winapi::shared::minwindef::{WORD, DWORD, HMODULE, FARPROC}; 3 | use self::winapi::shared::ntdef::WCHAR; 4 | use self::winapi::shared::winerror; 5 | use self::winapi::um::{errhandlingapi, libloaderapi}; 6 | 7 | use util::{ensure_compatible_types, cstr_cow_from_bytes}; 8 | 9 | use std::ffi::{OsStr, OsString}; 10 | use std::{fmt, io, marker, mem, ptr}; 11 | use std::os::windows::ffi::{OsStrExt, OsStringExt}; 12 | use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}; 13 | 14 | 15 | /// A platform-specific equivalent of the cross-platform `Library`. 16 | pub struct Library(HMODULE); 17 | 18 | unsafe impl Send for Library {} 19 | // Now, this is sort-of-tricky. MSDN documentation does not really make any claims as to safety of 20 | // the Win32 APIs. Sadly, whomever I asked, even current and former Microsoft employees, couldn’t 21 | // say for sure, whether the Win32 APIs used to implement `Library` are thread-safe or not. 22 | // 23 | // My investigation ended up with a question about thread-safety properties of the API involved 24 | // being sent to an internal (to MS) general question mailing-list. The conclusion of the mail is 25 | // as such: 26 | // 27 | // * Nobody inside MS (at least out of all the people who have seen the question) knows for 28 | // sure either; 29 | // * However, the general consensus between MS developers is that one can rely on the API being 30 | // thread-safe. In case it is not thread-safe it should be considered a bug on the Windows 31 | // part. (NB: bugs filled at https://connect.microsoft.com/ against Windows Server) 32 | unsafe impl Sync for Library {} 33 | 34 | impl Library { 35 | /// Find and load a shared library (module). 36 | /// 37 | /// Corresponds to `LoadLibraryW(filename)`. 38 | #[inline] 39 | pub fn new>(filename: P) -> ::Result { 40 | let wide_filename: Vec = filename.as_ref().encode_wide().chain(Some(0)).collect(); 41 | let _guard = ErrorModeGuard::new(); 42 | 43 | let ret = with_get_last_error(|| { 44 | // Make sure no winapi calls as a result of drop happen inside this closure, because 45 | // otherwise that might change the return value of the GetLastError. 46 | let handle = unsafe { libloaderapi::LoadLibraryW(wide_filename.as_ptr()) }; 47 | if handle.is_null() { 48 | None 49 | } else { 50 | Some(Library(handle)) 51 | } 52 | }).map_err(|e| e.unwrap_or_else(|| 53 | panic!("LoadLibraryW failed but GetLastError did not report the error") 54 | )); 55 | 56 | drop(wide_filename); // Drop wide_filename here to ensure it doesn’t get moved and dropped 57 | // inside the closure by mistake. See comment inside the closure. 58 | ret 59 | } 60 | 61 | /// Get a pointer to function or static variable by symbol name. 62 | /// 63 | /// The `symbol` may not contain any null bytes, with an exception of last byte. A null 64 | /// terminated `symbol` may avoid a string allocation in some cases. 65 | /// 66 | /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are 67 | /// most likely invalid. 68 | /// 69 | /// ## Unsafety 70 | /// 71 | /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is 72 | /// undefined. 73 | pub unsafe fn get(&self, symbol: &[u8]) -> ::Result> { 74 | ensure_compatible_types::(); 75 | let symbol = try!(cstr_cow_from_bytes(symbol)); 76 | with_get_last_error(|| { 77 | let symbol = libloaderapi::GetProcAddress(self.0, symbol.as_ptr()); 78 | if symbol.is_null() { 79 | None 80 | } else { 81 | Some(Symbol { 82 | pointer: symbol, 83 | pd: marker::PhantomData 84 | }) 85 | } 86 | }).map_err(|e| e.unwrap_or_else(|| 87 | panic!("GetProcAddress failed but GetLastError did not report the error") 88 | )) 89 | } 90 | 91 | /// Get a pointer to function or static variable by ordinal number. 92 | /// 93 | /// ## Unsafety 94 | /// 95 | /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is 96 | /// undefined. 97 | pub unsafe fn get_ordinal(&self, ordinal: WORD) -> ::Result> { 98 | ensure_compatible_types::(); 99 | with_get_last_error(|| { 100 | let ordinal = ordinal as usize as *mut _; 101 | let symbol = libloaderapi::GetProcAddress(self.0, ordinal); 102 | if symbol.is_null() { 103 | None 104 | } else { 105 | Some(Symbol { 106 | pointer: symbol, 107 | pd: marker::PhantomData 108 | }) 109 | } 110 | }).map_err(|e| e.unwrap_or_else(|| 111 | panic!("GetProcAddress failed but GetLastError did not report the error") 112 | )) 113 | } 114 | } 115 | 116 | impl Drop for Library { 117 | fn drop(&mut self) { 118 | with_get_last_error(|| { 119 | if unsafe { libloaderapi::FreeLibrary(self.0) == 0 } { 120 | None 121 | } else { 122 | Some(()) 123 | } 124 | }).unwrap() 125 | } 126 | } 127 | 128 | impl fmt::Debug for Library { 129 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 130 | unsafe { 131 | let mut buf: [WCHAR; 1024] = mem::uninitialized(); 132 | let len = libloaderapi::GetModuleFileNameW(self.0, 133 | (&mut buf[..]).as_mut_ptr(), 1024) as usize; 134 | if len == 0 { 135 | f.write_str(&format!("Library@{:p}", self.0)) 136 | } else { 137 | let string: OsString = OsString::from_wide(&buf[..len]); 138 | f.write_str(&format!("Library@{:p} from {:?}", self.0, string)) 139 | } 140 | } 141 | } 142 | } 143 | 144 | /// Symbol from a library. 145 | /// 146 | /// A major difference compared to the cross-platform `Symbol` is that this does not ensure the 147 | /// `Symbol` does not outlive `Library` it comes from. 148 | pub struct Symbol { 149 | pointer: FARPROC, 150 | pd: marker::PhantomData 151 | } 152 | 153 | impl Symbol> { 154 | /// Lift Option out of the symbol. 155 | pub fn lift_option(self) -> Option> { 156 | if self.pointer.is_null() { 157 | None 158 | } else { 159 | Some(Symbol { 160 | pointer: self.pointer, 161 | pd: marker::PhantomData, 162 | }) 163 | } 164 | } 165 | } 166 | 167 | unsafe impl Send for Symbol {} 168 | unsafe impl Sync for Symbol {} 169 | 170 | impl Clone for Symbol { 171 | fn clone(&self) -> Symbol { 172 | Symbol { ..*self } 173 | } 174 | } 175 | 176 | impl ::std::ops::Deref for Symbol { 177 | type Target = T; 178 | fn deref(&self) -> &T { 179 | unsafe { 180 | // Additional reference level for a dereference on `deref` return value. 181 | mem::transmute(&self.pointer) 182 | } 183 | } 184 | } 185 | 186 | impl fmt::Debug for Symbol { 187 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 188 | f.write_str(&format!("Symbol@{:p}", self.pointer)) 189 | } 190 | } 191 | 192 | 193 | static USE_ERRORMODE: AtomicBool = ATOMIC_BOOL_INIT; 194 | struct ErrorModeGuard(DWORD); 195 | 196 | impl ErrorModeGuard { 197 | fn new() -> Option { 198 | const SEM_FAILCE: DWORD = 1; 199 | unsafe { 200 | if !USE_ERRORMODE.load(Ordering::Acquire) { 201 | let mut previous_mode = 0; 202 | let success = errhandlingapi::SetThreadErrorMode(SEM_FAILCE, &mut previous_mode) != 0; 203 | if !success && errhandlingapi::GetLastError() == winerror::ERROR_CALL_NOT_IMPLEMENTED { 204 | USE_ERRORMODE.store(true, Ordering::Release); 205 | } else if !success { 206 | // SetThreadErrorMode failed with some other error? How in the world is it 207 | // possible for what is essentially a simple variable swap to fail? 208 | // For now we just ignore the error -- the worst that can happen here is 209 | // the previous mode staying on and user seeing a dialog error on older Windows 210 | // machines. 211 | return None; 212 | } else if previous_mode == SEM_FAILCE { 213 | return None; 214 | } else { 215 | return Some(ErrorModeGuard(previous_mode)); 216 | } 217 | } 218 | match errhandlingapi::SetErrorMode(SEM_FAILCE) { 219 | SEM_FAILCE => { 220 | // This is important to reduce racy-ness when this library is used on multiple 221 | // threads. In particular this helps with following race condition: 222 | // 223 | // T1: SetErrorMode(SEM_FAILCE) 224 | // T2: SetErrorMode(SEM_FAILCE) 225 | // T1: SetErrorMode(old_mode) # not SEM_FAILCE 226 | // T2: SetErrorMode(SEM_FAILCE) # restores to SEM_FAILCE on drop 227 | // 228 | // This is still somewhat racy in a sense that T1 might restore the error 229 | // mode before T2 finishes loading the library, but that is less of a 230 | // concern – it will only end up in end user seeing a dialog. 231 | // 232 | // Also, SetErrorMode itself is probably not an atomic operation. 233 | None 234 | } 235 | a => Some(ErrorModeGuard(a)) 236 | } 237 | } 238 | } 239 | } 240 | 241 | impl Drop for ErrorModeGuard { 242 | fn drop(&mut self) { 243 | unsafe { 244 | if !USE_ERRORMODE.load(Ordering::Relaxed) { 245 | errhandlingapi::SetThreadErrorMode(self.0, ptr::null_mut()); 246 | } else { 247 | errhandlingapi::SetErrorMode(self.0); 248 | } 249 | } 250 | } 251 | } 252 | 253 | fn with_get_last_error(closure: F) -> Result> 254 | where F: FnOnce() -> Option { 255 | closure().ok_or_else(|| { 256 | let error = unsafe { errhandlingapi::GetLastError() }; 257 | if error == 0 { 258 | None 259 | } else { 260 | Some(io::Error::from_raw_os_error(error as i32)) 261 | } 262 | }) 263 | } 264 | 265 | #[test] 266 | fn works_getlasterror() { 267 | let lib = Library::new("kernel32.dll").unwrap(); 268 | let gle: Symbol DWORD> = unsafe { 269 | lib.get(b"GetLastError").unwrap() 270 | }; 271 | unsafe { 272 | errhandlingapi::SetLastError(42); 273 | assert_eq!(errhandlingapi::GetLastError(), gle()) 274 | } 275 | } 276 | 277 | #[test] 278 | fn works_getlasterror0() { 279 | let lib = Library::new("kernel32.dll").unwrap(); 280 | let gle: Symbol DWORD> = unsafe { 281 | lib.get(b"GetLastError\0").unwrap() 282 | }; 283 | unsafe { 284 | errhandlingapi::SetLastError(42); 285 | assert_eq!(errhandlingapi::GetLastError(), gle()) 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/os/unix/mod.rs: -------------------------------------------------------------------------------- 1 | use util::{ensure_compatible_types, cstr_cow_from_bytes}; 2 | 3 | use std::ffi::{CStr, OsStr}; 4 | use std::{fmt, io, marker, mem, ptr}; 5 | use std::os::raw; 6 | use std::os::unix::ffi::OsStrExt; 7 | 8 | extern "C" { 9 | fn rust_libloading_dlerror_mutex_lock(); 10 | fn rust_libloading_dlerror_mutex_unlock(); 11 | } 12 | 13 | struct DlerrorMutexGuard(()); 14 | 15 | impl DlerrorMutexGuard { 16 | fn new() -> DlerrorMutexGuard { 17 | unsafe { 18 | rust_libloading_dlerror_mutex_lock(); 19 | } 20 | DlerrorMutexGuard(()) 21 | } 22 | } 23 | 24 | impl Drop for DlerrorMutexGuard { 25 | fn drop(&mut self) { 26 | unsafe { 27 | rust_libloading_dlerror_mutex_unlock(); 28 | } 29 | } 30 | } 31 | 32 | // libdl is crazy. 33 | // 34 | // First of all, whole error handling scheme in libdl is done via setting and querying some global 35 | // state, therefore it is not safe to use libdl in MT-capable environment at all. Only in POSIX 36 | // 2008+TC1 a thread-local state was allowed, which for our purposes is way too late. 37 | fn with_dlerror(closure: F) -> Result> 38 | where F: FnOnce() -> Option { 39 | // We will guard all uses of libdl library with our own mutex. This makes libdl 40 | // safe to use in MT programs provided the only way a program uses libdl is via this library. 41 | let _lock = DlerrorMutexGuard::new(); 42 | // While we could could call libdl here to clear the previous error value, only the dlsym 43 | // depends on it being cleared beforehand and only in some cases too. We will instead clear the 44 | // error inside the dlsym binding instead. 45 | // 46 | // In all the other cases, clearing the error here will only be hiding misuse of these bindings 47 | // or the libdl. 48 | closure().ok_or_else(|| unsafe { 49 | // This code will only get executed if the `closure` returns `None`. 50 | let error = dlerror(); 51 | if error.is_null() { 52 | // In non-dlsym case this may happen when there’re bugs in our bindings or there’s 53 | // non-libloading user of libdl; possibly in another thread. 54 | None 55 | } else { 56 | // You can’t even rely on error string being static here; call to subsequent dlerror 57 | // may invalidate or overwrite the error message. Why couldn’t they simply give up the 58 | // ownership over the message? 59 | // TODO: should do locale-aware conversion here. OTOH Rust doesn’t seem to work well in 60 | // any system that uses non-utf8 locale, so I doubt there’s a problem here. 61 | let message = CStr::from_ptr(error).to_string_lossy().into_owned(); 62 | Some(io::Error::new(io::ErrorKind::Other, message)) 63 | // Since we do a copy of the error string above, maybe we should call dlerror again to 64 | // let libdl know it may free its copy of the string now? 65 | } 66 | }) 67 | } 68 | 69 | /// A platform-specific equivalent of the cross-platform `Library`. 70 | pub struct Library { 71 | handle: *mut raw::c_void 72 | } 73 | 74 | unsafe impl Send for Library {} 75 | 76 | // That being said... this section in the volume 2 of POSIX.1-2008 states: 77 | // 78 | // > All functions defined by this volume of POSIX.1-2008 shall be thread-safe, except that the 79 | // > following functions need not be thread-safe. 80 | // 81 | // With notable absence of any dl* function other than dlerror in the list. By “this volume” 82 | // I suppose they refer precisely to the “volume 2”. dl* family of functions are specified 83 | // by this same volume, so the conclusion is indeed that dl* functions are required by POSIX 84 | // to be thread-safe. Great! 85 | // 86 | // See for more details: 87 | // 88 | // * https://github.com/nagisa/rust_libloading/pull/17 89 | // * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09_01 90 | unsafe impl Sync for Library {} 91 | 92 | impl Library { 93 | /// Find and load a shared library (module). 94 | /// 95 | /// Locations where library is searched for is platform specific and can’t be adjusted 96 | /// portably. 97 | /// 98 | /// Corresponds to `dlopen(filename, RTLD_NOW)`. 99 | #[inline] 100 | pub fn new>(filename: P) -> ::Result { 101 | Library::open(Some(filename), RTLD_NOW) 102 | } 103 | 104 | /// Load the dynamic libraries linked into main program. 105 | /// 106 | /// This allows retrieving symbols from any **dynamic** library linked into the program, 107 | /// without specifying the exact library. 108 | /// 109 | /// Corresponds to `dlopen(NULL, RTLD_NOW)`. 110 | #[inline] 111 | pub fn this() -> Library { 112 | Library::open(None::<&OsStr>, RTLD_NOW).unwrap() 113 | } 114 | 115 | /// Find and load a shared library (module). 116 | /// 117 | /// Locations where library is searched for is platform specific and can’t be adjusted 118 | /// portably. 119 | /// 120 | /// If the `filename` is None, null pointer is passed to `dlopen`. 121 | /// 122 | /// Corresponds to `dlopen(filename, flags)`. 123 | pub fn open

(filename: Option

, flags: raw::c_int) -> ::Result 124 | where P: AsRef { 125 | let filename = match filename { 126 | None => None, 127 | Some(ref f) => Some(try!(cstr_cow_from_bytes(f.as_ref().as_bytes()))), 128 | }; 129 | with_dlerror(move || { 130 | let result = unsafe { 131 | let r = dlopen(match filename { 132 | None => ptr::null(), 133 | Some(ref f) => f.as_ptr() 134 | }, flags); 135 | // ensure filename lives until dlopen completes 136 | drop(filename); 137 | r 138 | }; 139 | if result.is_null() { 140 | None 141 | } else { 142 | Some(Library { 143 | handle: result 144 | }) 145 | } 146 | }).map_err(|e| e.unwrap_or_else(|| 147 | panic!("dlopen failed but dlerror did not report anything") 148 | )) 149 | } 150 | 151 | /// Get a pointer to function or static variable by symbol name. 152 | /// 153 | /// The `symbol` may not contain any null bytes, with an exception of last byte. A null 154 | /// terminated `symbol` may avoid a string allocation in some cases. 155 | /// 156 | /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are 157 | /// most likely invalid. 158 | /// 159 | /// ## Unsafety 160 | /// 161 | /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is 162 | /// undefined. 163 | /// 164 | /// ## Platform-specific behaviour 165 | /// 166 | /// OS X uses some sort of lazy initialization scheme, which makes loading TLS variables 167 | /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour. 168 | pub unsafe fn get(&self, symbol: &[u8]) -> ::Result> { 169 | ensure_compatible_types::(); 170 | let symbol = try!(cstr_cow_from_bytes(symbol)); 171 | // `dlsym` may return nullptr in two cases: when a symbol genuinely points to a null 172 | // pointer or the symbol cannot be found. In order to detect this case a double dlerror 173 | // pattern must be used, which is, sadly, a little bit racy. 174 | // 175 | // We try to leave as little space as possible for this to occur, but we can’t exactly 176 | // fully prevent it. 177 | match with_dlerror(|| { 178 | dlerror(); 179 | let symbol = dlsym(self.handle, symbol.as_ptr()); 180 | if symbol.is_null() { 181 | None 182 | } else { 183 | Some(Symbol { 184 | pointer: symbol, 185 | pd: marker::PhantomData 186 | }) 187 | } 188 | }) { 189 | Err(None) => Ok(Symbol { 190 | pointer: ptr::null_mut(), 191 | pd: marker::PhantomData 192 | }), 193 | Err(Some(e)) => Err(e), 194 | Ok(x) => Ok(x) 195 | } 196 | } 197 | } 198 | 199 | impl Drop for Library { 200 | fn drop(&mut self) { 201 | with_dlerror(|| if unsafe { dlclose(self.handle) } == 0 { 202 | Some(()) 203 | } else { 204 | None 205 | }).unwrap(); 206 | } 207 | } 208 | 209 | impl fmt::Debug for Library { 210 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 211 | f.write_str(&format!("Library@{:p}", self.handle)) 212 | } 213 | } 214 | 215 | /// Symbol from a library. 216 | /// 217 | /// A major difference compared to the cross-platform `Symbol` is that this does not ensure the 218 | /// `Symbol` does not outlive `Library` it comes from. 219 | pub struct Symbol { 220 | pointer: *mut raw::c_void, 221 | pd: marker::PhantomData 222 | } 223 | 224 | impl Symbol> { 225 | /// Lift Option out of the symbol. 226 | pub fn lift_option(self) -> Option> { 227 | if self.pointer.is_null() { 228 | None 229 | } else { 230 | Some(Symbol { 231 | pointer: self.pointer, 232 | pd: marker::PhantomData, 233 | }) 234 | } 235 | } 236 | } 237 | 238 | unsafe impl Send for Symbol {} 239 | unsafe impl Sync for Symbol {} 240 | 241 | impl Clone for Symbol { 242 | fn clone(&self) -> Symbol { 243 | Symbol { ..*self } 244 | } 245 | } 246 | 247 | impl ::std::ops::Deref for Symbol { 248 | type Target = T; 249 | fn deref(&self) -> &T { 250 | unsafe { 251 | // Additional reference level for a dereference on `deref` return value. 252 | mem::transmute(&self.pointer) 253 | } 254 | } 255 | } 256 | 257 | impl fmt::Debug for Symbol { 258 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 259 | unsafe { 260 | let mut info: DlInfo = mem::uninitialized(); 261 | if dladdr(self.pointer, &mut info) != 0 { 262 | if info.dli_sname.is_null() { 263 | f.write_str(&format!("Symbol@{:p} from {:?}", 264 | self.pointer, 265 | CStr::from_ptr(info.dli_fname))) 266 | } else { 267 | f.write_str(&format!("Symbol {:?}@{:p} from {:?}", 268 | CStr::from_ptr(info.dli_sname), self.pointer, 269 | CStr::from_ptr(info.dli_fname))) 270 | } 271 | } else { 272 | f.write_str(&format!("Symbol@{:p}", self.pointer)) 273 | } 274 | } 275 | } 276 | } 277 | 278 | // Platform specific things 279 | 280 | extern { 281 | fn dlopen(filename: *const raw::c_char, flags: raw::c_int) -> *mut raw::c_void; 282 | fn dlclose(handle: *mut raw::c_void) -> raw::c_int; 283 | fn dlsym(handle: *mut raw::c_void, symbol: *const raw::c_char) -> *mut raw::c_void; 284 | fn dlerror() -> *mut raw::c_char; 285 | fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int; 286 | } 287 | 288 | #[cfg(not(target_os="android"))] 289 | const RTLD_NOW: raw::c_int = 2; 290 | const RTLD_GLOBAL: raw::c_int = 0x00100; 291 | #[cfg(target_os="android")] 292 | const RTLD_NOW: raw::c_int = 0; 293 | 294 | #[repr(C)] 295 | struct DlInfo { 296 | dli_fname: *const raw::c_char, 297 | dli_fbase: *mut raw::c_void, 298 | dli_sname: *const raw::c_char, 299 | dli_saddr: *mut raw::c_void 300 | } 301 | 302 | #[test] 303 | fn this() { 304 | Library::this(); 305 | } 306 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A memory-safer wrapper around system dynamic library loading primitives. 2 | //! 3 | //! Using this library allows loading [dynamic libraries](struct.Library.html) (also known as 4 | //! shared libraries) as well as use functions and static variables these libraries contain. 5 | //! 6 | //! While the library does expose a cross-platform interface to load a library and find stuff 7 | //! inside it, little is done to paper over the platform differences, especially where library 8 | //! loading is involved. The documentation for each function will attempt to document such 9 | //! differences on the best-effort basis. 10 | //! 11 | //! Less safe, platform specific bindings are also available. See the 12 | //! [`os::platform`](os/index.html) module for details. 13 | //! 14 | //! # Usage 15 | //! 16 | //! Add a dependency on this library to your `Cargo.toml`: 17 | //! 18 | //! ```toml 19 | //! [dependencies] 20 | //! libloading = "0.5" 21 | //! ``` 22 | //! 23 | //! Then inside your project 24 | //! 25 | //! ```no_run 26 | //! extern crate libloading as lib; 27 | //! 28 | //! fn call_dynamic() -> lib::Result { 29 | //! let lib = lib::Library::new("/path/to/liblibrary.so")?; 30 | //! unsafe { 31 | //! let func: lib::Symbol u32> = lib.get(b"my_func")?; 32 | //! Ok(func()) 33 | //! } 34 | //! } 35 | //! ``` 36 | //! 37 | //! The compiler will ensure that the loaded `function` will not outlive the `Library` it comes 38 | //! from, preventing a common cause of undefined behaviour and memory safety problems. 39 | use std::ffi::OsStr; 40 | use std::fmt; 41 | use std::ops; 42 | use std::marker; 43 | 44 | #[cfg(unix)] 45 | use self::os::unix as imp; 46 | #[cfg(windows)] 47 | use self::os::windows as imp; 48 | 49 | pub mod os; 50 | pub mod changelog; 51 | mod util; 52 | 53 | pub type Result = ::std::io::Result; 54 | 55 | /// A loaded dynamic library. 56 | pub struct Library(imp::Library); 57 | 58 | impl Library { 59 | /// Find and load a dynamic library. 60 | /// 61 | /// The `filename` argument may be any of: 62 | /// 63 | /// * A library filename; 64 | /// * Absolute path to the library; 65 | /// * Relative (to the current working directory) path to the library. 66 | /// 67 | /// ## Thread-safety 68 | /// 69 | /// The implementation strives to be as MT-safe as sanely possible, however due to certain 70 | /// error-handling related resources not always being safe, this library is not MT-safe either. 71 | /// 72 | /// * On Windows Vista and earlier error handling falls back to [`SetErrorMode`], which is not 73 | /// MT-safe. MT-scenarios involving this function may cause a traditional data race; 74 | /// * On some UNIX targets `dlerror` might not be MT-safe, resulting in garbage error messages 75 | /// in certain MT-scenarios. 76 | /// 77 | /// [`SetErrorMode`]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680621(v=vs.85).aspx 78 | /// 79 | /// Calling this function from multiple threads is not safe if used in conjunction with 80 | /// path-less filename and library search path is modified (`SetDllDirectory` function on 81 | /// Windows, `{DY,}LD_LIBRARY_PATH` environment variable on UNIX). 82 | /// 83 | /// ## Platform-specific behaviour 84 | /// 85 | /// When a plain library filename is supplied, locations where library is searched for is 86 | /// platform specific and cannot be adjusted in a portable manner. 87 | /// 88 | /// ### Windows 89 | /// 90 | /// If the `filename` specifies a library filename without path and with extension omitted, 91 | /// `.dll` extension is implicitly added. This behaviour may be suppressed by appending a 92 | /// trailing `.` to the `filename`. 93 | /// 94 | /// If the library contains thread local variables (MSVC’s `_declspec(thread)`, Rust’s 95 | /// `#[thread_local]` attributes), loading the library will fail on versions prior to Windows 96 | /// Vista. 97 | /// 98 | /// ## Tips 99 | /// 100 | /// Distributing your dynamic libraries under a filename common to all platforms (e.g. 101 | /// `awesome.module`) allows to avoid code which has to account for platform’s conventional 102 | /// library filenames. 103 | /// 104 | /// Strive to specify absolute or relative path to your library, unless system-wide libraries 105 | /// are being loaded. Platform-dependent library search locations combined with various quirks 106 | /// related to path-less filenames may cause flaky code. 107 | /// 108 | /// ## Examples 109 | /// 110 | /// ```no_run 111 | /// # use ::libloading::Library; 112 | /// // Any of the following are valid. 113 | /// let _ = Library::new("/path/to/awesome.module").unwrap(); 114 | /// let _ = Library::new("../awesome.module").unwrap(); 115 | /// let _ = Library::new("libsomelib.so.1").unwrap(); 116 | /// ``` 117 | pub fn new>(filename: P) -> Result { 118 | imp::Library::new(filename).map(From::from) 119 | } 120 | 121 | /// Get a pointer to function or static variable by symbol name. 122 | /// 123 | /// The `symbol` may not contain any null bytes, with an exception of last byte. A null 124 | /// terminated `symbol` may avoid a string allocation in some cases. 125 | /// 126 | /// Symbol is interpreted as-is; no mangling is done. This means that symbols like `x::y` are 127 | /// most likely invalid. 128 | /// 129 | /// ## Unsafety 130 | /// 131 | /// Pointer to a value of arbitrary type is returned. Using a value with wrong type is 132 | /// undefined. 133 | /// 134 | /// ## Platform-specific behaviour 135 | /// 136 | /// On Linux and Windows, a TLS variable acts just like any regular static variable. OS X uses 137 | /// some sort of lazy initialization scheme, which makes loading TLS variables this way 138 | /// impossible. Using a TLS variable loaded this way on OS X is undefined behaviour. 139 | /// 140 | /// ## Examples 141 | /// 142 | /// Given a loaded library: 143 | /// 144 | /// ```no_run 145 | /// # use ::libloading::Library; 146 | /// let lib = Library::new("/path/to/awesome.module").unwrap(); 147 | /// ``` 148 | /// 149 | /// Loading and using a function looks like this: 150 | /// 151 | /// ```no_run 152 | /// # use ::libloading::{Library, Symbol}; 153 | /// # let lib = Library::new("/path/to/awesome.module").unwrap(); 154 | /// unsafe { 155 | /// let awesome_function: Symbol f64> = 156 | /// lib.get(b"awesome_function\0").unwrap(); 157 | /// awesome_function(0.42); 158 | /// } 159 | /// ``` 160 | /// 161 | /// A static variable may also be loaded and inspected: 162 | /// 163 | /// ```no_run 164 | /// # use ::libloading::{Library, Symbol}; 165 | /// # let lib = Library::new("/path/to/awesome.module").unwrap(); 166 | /// unsafe { 167 | /// let awesome_variable: Symbol<*mut f64> = lib.get(b"awesome_variable\0").unwrap(); 168 | /// **awesome_variable = 42.0; 169 | /// }; 170 | /// ``` 171 | pub unsafe fn get<'lib, T>(&'lib self, symbol: &[u8]) -> Result> { 172 | self.0.get(symbol).map(|from| Symbol::from_raw(from, self)) 173 | } 174 | 175 | #[cfg(unix)] 176 | pub fn new_with_flags>(filename: P, flags: i32) -> Result { 177 | imp::Library::open(Some(filename), flags).map(From::from) 178 | } 179 | 180 | #[cfg(windows)] 181 | pub fn new_with_flags>(filename: P, _flags: i32) -> Result { 182 | imp::Library::new(filename).map(From::from) 183 | } 184 | } 185 | 186 | impl fmt::Debug for Library { 187 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 188 | self.0.fmt(f) 189 | } 190 | } 191 | 192 | impl From for Library { 193 | fn from(lib: imp::Library) -> Library { 194 | Library(lib) 195 | } 196 | } 197 | 198 | impl From for imp::Library { 199 | fn from(lib: Library) -> imp::Library { 200 | lib.0 201 | } 202 | } 203 | 204 | unsafe impl Send for Library {} 205 | unsafe impl Sync for Library {} 206 | 207 | /// Symbol from a library. 208 | /// 209 | /// This type is a safeguard against using dynamically loaded symbols after a `Library` is 210 | /// unloaded. Primary method to create an instance of a `Symbol` is via `Library::get`. 211 | /// 212 | /// Due to implementation of the `Deref` trait, an instance of `Symbol` may be used as if it was a 213 | /// function or variable directly, without taking care to “extract” function or variable manually 214 | /// most of the time. 215 | /// 216 | /// See [`Library::get`] for details. 217 | /// 218 | /// [`Library::get`]: ./struct.Library.html#method.get 219 | pub struct Symbol<'lib, T: 'lib> { 220 | inner: imp::Symbol, 221 | pd: marker::PhantomData<&'lib T> 222 | } 223 | 224 | impl<'lib, T> Symbol<'lib, T> { 225 | /// Extract the wrapped `os::platform::Symbol`. 226 | /// 227 | /// ## Unsafety 228 | /// Using this function relinquishes all the lifetime guarantees. It is up to programmer to 229 | /// ensure the resulting `Symbol` is not used past the lifetime of the `Library` this symbol 230 | /// was loaded from. 231 | /// 232 | /// ## Examples 233 | /// 234 | /// ```no_run 235 | /// # use ::libloading::{Library, Symbol}; 236 | /// let lib = Library::new("/path/to/awesome.module").unwrap(); 237 | /// unsafe { 238 | /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); 239 | /// let symbol = symbol.into_raw(); 240 | /// } 241 | /// ``` 242 | pub unsafe fn into_raw(self) -> imp::Symbol { 243 | self.inner 244 | } 245 | 246 | /// Wrap the `os::platform::Symbol` into this safe wrapper. 247 | /// 248 | /// Note that, in order to create association between the symbol and the library this symbol 249 | /// came from, this function requires reference to the library provided. 250 | /// 251 | /// ## Unsafety 252 | /// 253 | /// It is invalid to provide a reference to any other value other than the library the `sym` 254 | /// was loaded from. Doing so invalidates any lifetime guarantees. 255 | /// 256 | /// ## Examples 257 | /// 258 | /// ```no_run 259 | /// # use ::libloading::{Library, Symbol}; 260 | /// let lib = Library::new("/path/to/awesome.module").unwrap(); 261 | /// unsafe { 262 | /// let symbol: Symbol<*mut u32> = lib.get(b"symbol\0").unwrap(); 263 | /// let symbol = symbol.into_raw(); 264 | /// let symbol = Symbol::from_raw(symbol, &lib); 265 | /// } 266 | /// ``` 267 | pub unsafe fn from_raw(sym: imp::Symbol, _: &'lib L) -> Symbol<'lib, T> { 268 | Symbol { 269 | inner: sym, 270 | pd: marker::PhantomData 271 | } 272 | } 273 | } 274 | 275 | impl<'lib, T> Symbol<'lib, Option> { 276 | /// Lift Option out of the symbol. 277 | /// 278 | /// ## Examples 279 | /// 280 | /// ```no_run 281 | /// # use ::libloading::{Library, Symbol}; 282 | /// let lib = Library::new("/path/to/awesome.module").unwrap(); 283 | /// unsafe { 284 | /// let symbol: Symbol> = lib.get(b"symbol\0").unwrap(); 285 | /// let symbol: Symbol<*mut u32> = symbol.lift_option().expect("static is not null"); 286 | /// } 287 | /// ``` 288 | pub fn lift_option(self) -> Option> { 289 | self.inner.lift_option().map(|is| Symbol { 290 | inner: is, 291 | pd: marker::PhantomData, 292 | }) 293 | } 294 | } 295 | 296 | impl<'lib, T> Clone for Symbol<'lib, T> { 297 | fn clone(&self) -> Symbol<'lib, T> { 298 | Symbol { 299 | inner: self.inner.clone(), 300 | pd: marker::PhantomData 301 | } 302 | } 303 | } 304 | 305 | // FIXME: implement FnOnce for callable stuff instead. 306 | impl<'lib, T> ops::Deref for Symbol<'lib, T> { 307 | type Target = T; 308 | fn deref(&self) -> &T { 309 | ops::Deref::deref(&self.inner) 310 | } 311 | } 312 | 313 | impl<'lib, T> fmt::Debug for Symbol<'lib, T> { 314 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 315 | self.inner.fmt(f) 316 | } 317 | } 318 | 319 | unsafe impl<'lib, T: Send> Send for Symbol<'lib, T> {} 320 | unsafe impl<'lib, T: Sync> Sync for Symbol<'lib, T> {} 321 | --------------------------------------------------------------------------------