1 // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Dynamic library facilities.
13 //! A simple wrapper over the platform's dynamic library facilities
16 use std
::ffi
::{CString, OsString}
;
17 use std
::path
::{Path, PathBuf}
;
19 pub struct DynamicLibrary
{
23 impl Drop
for DynamicLibrary
{
26 dl
::close(self.handle
)
32 /// Lazily open a dynamic library. When passed None it gives a
33 /// handle to the calling process
34 pub fn open(filename
: Option
<&Path
>) -> Result
<DynamicLibrary
, String
> {
35 let maybe_library
= dl
::open(filename
.map(|path
| path
.as_os_str()));
37 // The dynamic library must not be constructed if there is
38 // an error opening the library so the destructor does not
42 Ok(handle
) => Ok(DynamicLibrary { handle: handle }
)
46 /// Prepends a path to this process's search path for dynamic libraries
47 pub fn prepend_search_path(path
: &Path
) {
48 let mut search_path
= DynamicLibrary
::search_path();
49 search_path
.insert(0, path
.to_path_buf());
50 env
::set_var(DynamicLibrary
::envvar(), &DynamicLibrary
::create_path(&search_path
));
53 /// From a slice of paths, create a new vector which is suitable to be an
54 /// environment variable for this platforms dylib search path.
55 pub fn create_path(path
: &[PathBuf
]) -> OsString
{
56 let mut newvar
= OsString
::new();
57 for (i
, path
) in path
.iter().enumerate() {
58 if i
> 0 { newvar.push(DynamicLibrary::separator()); }
64 /// Returns the environment variable for this process's dynamic library
66 pub fn envvar() -> &'
static str {
69 } else if cfg
!(target_os
= "macos") {
76 fn separator() -> &'
static str {
77 if cfg
!(windows
) { ";" }
else { ":" }
80 /// Returns the current search path for dynamic libraries being used by this
82 pub fn search_path() -> Vec
<PathBuf
> {
83 match env
::var_os(DynamicLibrary
::envvar()) {
84 Some(var
) => env
::split_paths(&var
).collect(),
89 /// Accesses the value at the symbol of the dynamic library.
90 pub unsafe fn symbol
<T
>(&self, symbol
: &str) -> Result
<*mut T
, String
> {
91 // This function should have a lifetime constraint of 'a on
92 // T but that feature is still unimplemented
94 let raw_string
= CString
::new(symbol
).unwrap();
95 let maybe_symbol_value
= dl
::symbol(self.handle
, raw_string
.as_ptr());
97 // The value must not be constructed if there is an error so
98 // the destructor does not run.
99 match maybe_symbol_value
{
100 Err(err
) => Err(err
),
101 Ok(symbol_value
) => Ok(symbol_value
as *mut T
)
113 fn test_loading_cosine() {
118 // The math library does not need to be loaded since it is already
119 // statically linked in
120 let libm
= match DynamicLibrary
::open(None
) {
121 Err(error
) => panic
!("Could not load self as module: {}", error
),
125 let cosine
: extern fn(libc
::c_double
) -> libc
::c_double
= unsafe {
126 match libm
.symbol("cos") {
127 Err(error
) => panic
!("Could not load function cos: {}", error
),
128 Ok(cosine
) => mem
::transmute
::<*mut u8, _
>(cosine
)
133 let expected_result
= 1.0;
134 let result
= cosine(argument
);
135 if result
!= expected_result
{
136 panic
!("cos({}) != {} but equaled {} instead", argument
,
137 expected_result
, result
)
142 fn test_errors_do_not_crash() {
149 // Open /dev/null as a library to get an error, and make sure
150 // that only causes an error, and not a crash.
151 let path
= Path
::new("/dev/null");
152 match DynamicLibrary
::open(Some(&path
)) {
154 Ok(_
) => panic
!("Successfully opened the empty library.")
162 use std
::ffi
::{CStr, OsStr, CString}
;
163 use std
::os
::unix
::prelude
::*;
167 pub fn open(filename
: Option
<&OsStr
>) -> Result
<*mut u8, String
> {
168 check_for_errors_in(|| {
171 Some(filename
) => open_external(filename
),
172 None
=> open_internal(),
178 const LAZY
: libc
::c_int
= 1;
180 unsafe fn open_external(filename
: &OsStr
) -> *mut u8 {
181 let s
= CString
::new(filename
.as_bytes()).unwrap();
182 libc
::dlopen(s
.as_ptr(), LAZY
) as *mut u8
185 unsafe fn open_internal() -> *mut u8 {
186 libc
::dlopen(ptr
::null(), LAZY
) as *mut u8
189 pub fn check_for_errors_in
<T
, F
>(f
: F
) -> Result
<T
, String
> where
192 use std
::sync
::StaticMutex
;
193 static LOCK
: StaticMutex
= StaticMutex
::new();
195 // dlerror isn't thread safe, so we need to lock around this entire
197 let _guard
= LOCK
.lock();
198 let _old_error
= libc
::dlerror();
202 let last_error
= libc
::dlerror() as *const _
;
203 let ret
= if ptr
::null() == last_error
{
206 let s
= CStr
::from_ptr(last_error
).to_bytes();
207 Err(str::from_utf8(s
).unwrap().to_owned())
214 pub unsafe fn symbol(handle
: *mut u8,
215 symbol
: *const libc
::c_char
)
216 -> Result
<*mut u8, String
> {
217 check_for_errors_in(|| {
218 libc
::dlsym(handle
as *mut libc
::c_void
, symbol
) as *mut u8
221 pub unsafe fn close(handle
: *mut u8) {
222 libc
::dlclose(handle
as *mut libc
::c_void
); ()
230 use std
::os
::windows
::prelude
::*;
233 use libc
::{c_uint, c_void, c_char}
;
236 type HMODULE
= *mut u8;
238 type LPCWSTR
= *const u16;
239 type LPCSTR
= *const i8;
242 fn SetThreadErrorMode(dwNewMode
: DWORD
,
243 lpOldMode
: *mut DWORD
) -> c_uint
;
244 fn LoadLibraryW(name
: LPCWSTR
) -> HMODULE
;
245 fn GetModuleHandleExW(dwFlags
: DWORD
,
247 handle
: *mut HMODULE
) -> BOOL
;
248 fn GetProcAddress(handle
: HMODULE
,
249 name
: LPCSTR
) -> *mut c_void
;
250 fn FreeLibrary(handle
: HMODULE
) -> BOOL
;
253 pub fn open(filename
: Option
<&OsStr
>) -> Result
<*mut u8, String
> {
254 // disable "dll load failed" error dialog.
255 let prev_error_mode
= unsafe {
256 // SEM_FAILCRITICALERRORS 0x01
257 let new_error_mode
= 1;
258 let mut prev_error_mode
= 0;
259 let result
= SetThreadErrorMode(new_error_mode
,
260 &mut prev_error_mode
);
262 return Err(io
::Error
::last_os_error().to_string())
267 let result
= match filename
{
269 let filename_str
: Vec
<_
> =
270 filename
.encode_wide().chain(Some(0)).collect();
271 let result
= unsafe {
272 LoadLibraryW(filename_str
.as_ptr())
277 let mut handle
= ptr
::null_mut();
278 let succeeded
= unsafe {
279 GetModuleHandleExW(0 as DWORD
, ptr
::null(), &mut handle
)
282 Err(io
::Error
::last_os_error().to_string())
284 Ok(handle
as *mut u8)
290 SetThreadErrorMode(prev_error_mode
, ptr
::null_mut());
296 pub unsafe fn symbol(handle
: *mut u8,
297 symbol
: *const c_char
)
298 -> Result
<*mut u8, String
> {
299 let ptr
= GetProcAddress(handle
as HMODULE
, symbol
) as *mut u8;
303 pub unsafe fn close(handle
: *mut u8) {
304 FreeLibrary(handle
as HMODULE
);
307 fn ptr_result
<T
>(ptr
: *mut T
) -> Result
<*mut T
, String
> {
309 Err(io
::Error
::last_os_error().to_string())