rust/src/libstd/dynamic_lib.rs

375 lines
12 KiB
Rust
Raw Normal View History

2015-01-29 01:19:28 -06:00
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Dynamic library facilities.
//!
//! A simple wrapper over the platform's dynamic library facilities
2015-06-09 20:15:22 -05:00
#![unstable(feature = "dynamic_lib",
reason = "API has not been scrutinized and is highly likely to \
either disappear or change",
issue = "27810")]
2014-10-27 17:37:07 -05:00
#![allow(missing_docs)]
use prelude::v1::*;
use env;
use ffi::{CString, OsString};
use path::{Path, PathBuf};
pub struct DynamicLibrary {
handle: *mut u8
}
impl Drop for DynamicLibrary {
2013-09-16 20:18:07 -05:00
fn drop(&mut self) {
match dl::check_for_errors_in(|| {
unsafe {
dl::close(self.handle)
}
}) {
Ok(()) => {},
Err(str) => panic!("{}", str)
}
}
}
impl DynamicLibrary {
/// Lazily open a dynamic library. When passed None it gives a
/// handle to the calling process
pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
let maybe_library = dl::open(filename.map(|path| path.as_os_str()));
// The dynamic library must not be constructed if there is
// an error opening the library so the destructor does not
// run.
match maybe_library {
Err(err) => Err(err),
Ok(handle) => Ok(DynamicLibrary { handle: handle })
}
}
/// Prepends a path to this process's search path for dynamic libraries
pub fn prepend_search_path(path: &Path) {
let mut search_path = DynamicLibrary::search_path();
search_path.insert(0, path.to_path_buf());
env::set_var(DynamicLibrary::envvar(), &DynamicLibrary::create_path(&search_path));
}
/// From a slice of paths, create a new vector which is suitable to be an
/// environment variable for this platforms dylib search path.
pub fn create_path(path: &[PathBuf]) -> OsString {
let mut newvar = OsString::new();
for (i, path) in path.iter().enumerate() {
if i > 0 { newvar.push(DynamicLibrary::separator()); }
newvar.push(path);
}
return newvar;
}
/// Returns the environment variable for this process's dynamic library
/// search path
pub fn envvar() -> &'static str {
if cfg!(windows) {
"PATH"
} else if cfg!(target_os = "macos") {
"DYLD_LIBRARY_PATH"
} else {
"LD_LIBRARY_PATH"
}
}
fn separator() -> &'static str {
if cfg!(windows) { ";" } else { ":" }
}
/// Returns the current search path for dynamic libraries being used by this
/// process
pub fn search_path() -> Vec<PathBuf> {
match env::var_os(DynamicLibrary::envvar()) {
Some(var) => env::split_paths(&var).collect(),
std: Add a new `env` module This is an implementation of [RFC 578][rfc] which adds a new `std::env` module to replace most of the functionality in the current `std::os` module. More details can be found in the RFC itself, but as a summary the following methods have all been deprecated: [rfc]: https://github.com/rust-lang/rfcs/pull/578 * `os::args_as_bytes` => `env::args` * `os::args` => `env::args` * `os::consts` => `env::consts` * `os::dll_filename` => no replacement, use `env::consts` directly * `os::page_size` => `env::page_size` * `os::make_absolute` => use `env::current_dir` + `join` instead * `os::getcwd` => `env::current_dir` * `os::change_dir` => `env::set_current_dir` * `os::homedir` => `env::home_dir` * `os::tmpdir` => `env::temp_dir` * `os::join_paths` => `env::join_paths` * `os::split_paths` => `env::split_paths` * `os::self_exe_name` => `env::current_exe` * `os::self_exe_path` => use `env::current_exe` + `pop` * `os::set_exit_status` => `env::set_exit_status` * `os::get_exit_status` => `env::get_exit_status` * `os::env` => `env::vars` * `os::env_as_bytes` => `env::vars` * `os::getenv` => `env::var` or `env::var_string` * `os::getenv_as_bytes` => `env::var` * `os::setenv` => `env::set_var` * `os::unsetenv` => `env::remove_var` Many function signatures have also been tweaked for various purposes, but the main changes were: * `Vec`-returning APIs now all return iterators instead * All APIs are now centered around `OsString` instead of `Vec<u8>` or `String`. There is currently on convenience API, `env::var_string`, which can be used to get the value of an environment variable as a unicode `String`. All old APIs are `#[deprecated]` in-place and will remain for some time to allow for migrations. The semantics of the APIs have been tweaked slightly with regard to dealing with invalid unicode (panic instead of replacement). The new `std::env` module is all contained within the `env` feature, so crates must add the following to access the new APIs: #![feature(env)] [breaking-change]
2015-01-27 14:20:58 -06:00
None => Vec::new(),
}
}
/// Accesses the value at the symbol of the dynamic library.
2014-06-25 14:47:34 -05:00
pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
// This function should have a lifetime constraint of 'a on
// T but that feature is still unimplemented
std: Implement CString-related RFCs This commit is an implementation of [RFC 592][r592] and [RFC 840][r840]. These two RFCs tweak the behavior of `CString` and add a new `CStr` unsized slice type to the module. [r592]: https://github.com/rust-lang/rfcs/blob/master/text/0592-c-str-deref.md [r840]: https://github.com/rust-lang/rfcs/blob/master/text/0840-no-panic-in-c-string.md The new `CStr` type is only constructable via two methods: 1. By `deref`'ing from a `CString` 2. Unsafely via `CStr::from_ptr` The purpose of `CStr` is to be an unsized type which is a thin pointer to a `libc::c_char` (currently it is a fat pointer slice due to implementation limitations). Strings from C can be safely represented with a `CStr` and an appropriate lifetime as well. Consumers of `&CString` should now consume `&CStr` instead to allow producers to pass in C-originating strings instead of just Rust-allocated strings. A new constructor was added to `CString`, `new`, which takes `T: IntoBytes` instead of separate `from_slice` and `from_vec` methods (both have been deprecated in favor of `new`). The `new` method returns a `Result` instead of panicking. The error variant contains the relevant information about where the error happened and bytes (if present). Conversions are provided to the `io::Error` and `old_io::IoError` types via the `FromError` trait which translate to `InvalidInput`. This is a breaking change due to the modification of existing `#[unstable]` APIs and new deprecation, and more detailed information can be found in the two RFCs. Notable breakage includes: * All construction of `CString` now needs to use `new` and handle the outgoing `Result`. * Usage of `CString` as a byte slice now explicitly needs a `.as_bytes()` call. * The `as_slice*` methods have been removed in favor of just having the `as_bytes*` methods. Closes #22469 Closes #22470 [breaking-change]
2015-02-18 00:47:40 -06:00
let raw_string = CString::new(symbol).unwrap();
let maybe_symbol_value = dl::check_for_errors_in(|| {
dl::symbol(self.handle, raw_string.as_ptr())
});
// The value must not be constructed if there is an error so
// the destructor does not run.
match maybe_symbol_value {
Err(err) => Err(err),
Ok(symbol_value) => Ok(symbol_value as *mut T)
}
}
}
2014-09-29 00:31:50 -05:00
#[cfg(all(test, not(target_os = "ios")))]
mod tests {
use super::*;
use prelude::v1::*;
use libc;
use mem;
use path::Path;
#[test]
#[cfg_attr(any(windows,
target_os = "android", // FIXME #10379
target_env = "musl"), ignore)]
fn test_loading_cosine() {
// The math library does not need to be loaded since it is already
// statically linked in
let libm = match DynamicLibrary::open(None) {
Err(error) => panic!("Could not load self as module: {}", error),
Ok(libm) => libm
};
let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
match libm.symbol("cos") {
Err(error) => panic!("Could not load function cos: {}", error),
2014-06-25 14:47:34 -05:00
Ok(cosine) => mem::transmute::<*mut u8, _>(cosine)
}
};
let argument = 0.0;
let expected_result = 1.0;
let result = cosine(argument);
if result != expected_result {
panic!("cos({}) != {} but equaled {} instead", argument,
expected_result, result)
}
}
#[test]
2014-09-29 00:31:50 -05:00
#[cfg(any(target_os = "linux",
target_os = "macos",
target_os = "freebsd",
2015-01-29 01:19:28 -06:00
target_os = "dragonfly",
2015-01-17 01:51:04 -06:00
target_os = "bitrig",
2015-06-30 22:37:11 -05:00
target_os = "netbsd",
2015-01-29 01:19:28 -06:00
target_os = "openbsd"))]
fn test_errors_do_not_crash() {
// Open /dev/null as a library to get an error, and make sure
// that only causes an error, and not a crash.
let path = Path::new("/dev/null");
match DynamicLibrary::open(Some(&path)) {
Err(_) => {}
Ok(_) => panic!("Successfully opened the empty library.")
}
}
}
2014-09-29 00:31:50 -05:00
#[cfg(any(target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
2015-01-29 01:19:28 -06:00
target_os = "dragonfly",
2015-01-17 01:51:04 -06:00
target_os = "bitrig",
2015-06-30 22:37:11 -05:00
target_os = "netbsd",
2015-01-29 01:19:28 -06:00
target_os = "openbsd"))]
mod dl {
use prelude::v1::*;
use ffi::{CStr, OsStr};
use str;
use libc;
use ptr;
pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
check_for_errors_in(|| {
unsafe {
match filename {
Some(filename) => open_external(filename),
None => open_internal(),
}
}
})
}
const LAZY: libc::c_int = 1;
unsafe fn open_external(filename: &OsStr) -> *mut u8 {
let s = filename.to_cstring().unwrap();
dlopen(s.as_ptr(), LAZY) as *mut u8
}
unsafe fn open_internal() -> *mut u8 {
dlopen(ptr::null(), LAZY) as *mut u8
}
2014-12-07 13:15:25 -06:00
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
F: FnOnce() -> T,
{
use sync::StaticMutex;
static LOCK: StaticMutex = StaticMutex::new();
unsafe {
// dlerror isn't thread safe, so we need to lock around this entire
// sequence
let _guard = LOCK.lock();
let _old_error = dlerror();
let result = f();
2014-06-25 14:47:34 -05:00
let last_error = dlerror() as *const _;
let ret = if ptr::null() == last_error {
Ok(result)
} else {
std: Implement CString-related RFCs This commit is an implementation of [RFC 592][r592] and [RFC 840][r840]. These two RFCs tweak the behavior of `CString` and add a new `CStr` unsized slice type to the module. [r592]: https://github.com/rust-lang/rfcs/blob/master/text/0592-c-str-deref.md [r840]: https://github.com/rust-lang/rfcs/blob/master/text/0840-no-panic-in-c-string.md The new `CStr` type is only constructable via two methods: 1. By `deref`'ing from a `CString` 2. Unsafely via `CStr::from_ptr` The purpose of `CStr` is to be an unsized type which is a thin pointer to a `libc::c_char` (currently it is a fat pointer slice due to implementation limitations). Strings from C can be safely represented with a `CStr` and an appropriate lifetime as well. Consumers of `&CString` should now consume `&CStr` instead to allow producers to pass in C-originating strings instead of just Rust-allocated strings. A new constructor was added to `CString`, `new`, which takes `T: IntoBytes` instead of separate `from_slice` and `from_vec` methods (both have been deprecated in favor of `new`). The `new` method returns a `Result` instead of panicking. The error variant contains the relevant information about where the error happened and bytes (if present). Conversions are provided to the `io::Error` and `old_io::IoError` types via the `FromError` trait which translate to `InvalidInput`. This is a breaking change due to the modification of existing `#[unstable]` APIs and new deprecation, and more detailed information can be found in the two RFCs. Notable breakage includes: * All construction of `CString` now needs to use `new` and handle the outgoing `Result`. * Usage of `CString` as a byte slice now explicitly needs a `.as_bytes()` call. * The `as_slice*` methods have been removed in favor of just having the `as_bytes*` methods. Closes #22469 Closes #22470 [breaking-change]
2015-02-18 00:47:40 -06:00
let s = CStr::from_ptr(last_error).to_bytes();
2015-09-07 17:36:29 -05:00
Err(str::from_utf8(s).unwrap().to_owned())
};
ret
}
}
2014-06-25 14:47:34 -05:00
pub unsafe fn symbol(handle: *mut u8,
symbol: *const libc::c_char) -> *mut u8 {
dlsym(handle as *mut libc::c_void, symbol) as *mut u8
}
2014-06-25 14:47:34 -05:00
pub unsafe fn close(handle: *mut u8) {
dlclose(handle as *mut libc::c_void); ()
}
2015-01-29 01:19:28 -06:00
extern {
fn dlopen(filename: *const libc::c_char,
flag: libc::c_int) -> *mut libc::c_void;
fn dlerror() -> *mut libc::c_char;
fn dlsym(handle: *mut libc::c_void,
symbol: *const libc::c_char) -> *mut libc::c_void;
fn dlclose(handle: *mut libc::c_void) -> libc::c_int;
}
}
#[cfg(target_os = "windows")]
mod dl {
use prelude::v1::*;
use ffi::OsStr;
use libc;
use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
use sys::os;
use os::windows::prelude::*;
use ptr;
std: Fix Windows XP compatibility This commit enables executables linked against the standard library to run on Windows XP. There are two main components of this commit: * APIs not available on XP are shimmed to have a fallback implementation and use runtime detection to determine if they are available. * Mutexes on Windows were reimplemented to use critical sections on XP where rwlocks are not available. The APIs which are not available on XP are: * SetFileInformationByHandle - this is just used by `File::truncate` and that function just returns an error now. * SetThreadStackGuarantee - this is used by the stack overflow support on windows, but if this isn't available then it's just ignored (it seems non-critical). * All condition variable APIs are missing - the shims added for these apis simply always panic for now. We may eventually provide a fallback implementation, but for now the standard library does not rely on condition variables for normal use. * RWLocks, like condition variables, are missing entirely. The same story for condition variables is taken here. These APIs are all now panicking stubs as the standard library doesn't rely on RWLocks for normal use. Currently, as an optimization, we use SRWLOCKs for the standard `sync::Mutex` implementation on Windows, which is indeed required for normal operation of the standard library. To allow the standard library to run on XP, this commit reimplements mutexes on Windows to use SRWLOCK instances *if available* and otherwise a CriticalSection is used (with some checking for recursive locking). With all these changes put together, a 32-bit MSVC-built executable can run on Windows XP and print "hello world" Closes #12842 Closes #19992 Closes #24776
2015-06-26 11:30:35 -05:00
use sys::c::SetThreadErrorMode;
pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
// disable "dll load failed" error dialog.
let mut use_thread_mode = true;
let prev_error_mode = unsafe {
// SEM_FAILCRITICALERRORS 0x01
let new_error_mode = 1;
let mut prev_error_mode = 0;
// Windows >= 7 supports thread error mode.
let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
if result == 0 {
let err = os::errno();
if err as libc::c_int == ERROR_CALL_NOT_IMPLEMENTED {
use_thread_mode = false;
2015-03-18 11:36:18 -05:00
// SetThreadErrorMode not found. use fallback solution:
// SetErrorMode() Note that SetErrorMode is process-wide so
// this can cause race condition! However, since even
// Windows APIs do not care of such problem (#20650), we
// just assume SetErrorMode race is not a great deal.
prev_error_mode = SetErrorMode(new_error_mode);
}
}
prev_error_mode
};
unsafe {
SetLastError(0);
}
let result = match filename {
Some(filename) => {
let filename_str: Vec<_> =
filename.encode_wide().chain(Some(0)).collect();
let result = unsafe {
LoadLibraryW(filename_str.as_ptr() as *const libc::c_void)
};
// beware: Vec/String may change errno during drop!
// so we get error here.
if result == ptr::null_mut() {
let errno = os::errno();
Err(os::error_string(errno))
} else {
Ok(result as *mut u8)
}
}
None => {
let mut handle = ptr::null_mut();
let succeeded = unsafe {
GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle)
};
if succeeded == libc::FALSE {
let errno = os::errno();
Err(os::error_string(errno))
} else {
Ok(handle as *mut u8)
}
}
};
unsafe {
if use_thread_mode {
SetThreadErrorMode(prev_error_mode, ptr::null_mut());
} else {
SetErrorMode(prev_error_mode);
}
}
result
}
2014-12-07 13:15:25 -06:00
pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
F: FnOnce() -> T,
{
unsafe {
SetLastError(0);
let result = f();
let error = os::errno();
if 0 == error {
Ok(result)
} else {
Err(format!("Error code {}", error))
}
}
}
2014-06-25 14:47:34 -05:00
pub unsafe fn symbol(handle: *mut u8, symbol: *const libc::c_char) -> *mut u8 {
GetProcAddress(handle as *mut libc::c_void, symbol) as *mut u8
}
2014-06-25 14:47:34 -05:00
pub unsafe fn close(handle: *mut u8) {
FreeLibrary(handle as *mut libc::c_void); ()
}
#[allow(non_snake_case)]
2013-11-10 16:57:53 -06:00
extern "system" {
fn SetLastError(error: libc::size_t);
2014-06-25 14:47:34 -05:00
fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16,
handle: *mut *mut libc::c_void) -> libc::BOOL;
2014-06-25 14:47:34 -05:00
fn GetProcAddress(handle: *mut libc::c_void,
name: *const libc::c_char) -> *mut libc::c_void;
fn FreeLibrary(handle: *mut libc::c_void);
fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
}
}