]> git.lizzy.rs Git - rust.git/blob - src/libstd/dynamic_lib.rs
Merge branch 'fix-26673' of https://github.com/nhowell/rust into rollup_central
[rust.git] / src / libstd / dynamic_lib.rs
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.
4 //
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.
10
11 //! Dynamic library facilities.
12 //!
13 //! A simple wrapper over the platform's dynamic library facilities
14
15 #![unstable(feature = "dynamic_lib",
16             reason = "API has not been scrutinized and is highly likely to \
17                       either disappear or change")]
18 #![allow(missing_docs)]
19
20 use prelude::v1::*;
21
22 use env;
23 use ffi::{CString, OsString};
24 use mem;
25 use path::{Path, PathBuf};
26
27 pub struct DynamicLibrary {
28     handle: *mut u8
29 }
30
31 impl Drop for DynamicLibrary {
32     fn drop(&mut self) {
33         match dl::check_for_errors_in(|| {
34             unsafe {
35                 dl::close(self.handle)
36             }
37         }) {
38             Ok(()) => {},
39             Err(str) => panic!("{}", str)
40         }
41     }
42 }
43
44 impl DynamicLibrary {
45     /// Lazily open a dynamic library. When passed None it gives a
46     /// handle to the calling process
47     pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
48         let maybe_library = dl::open(filename.map(|path| path.as_os_str()));
49
50         // The dynamic library must not be constructed if there is
51         // an error opening the library so the destructor does not
52         // run.
53         match maybe_library {
54             Err(err) => Err(err),
55             Ok(handle) => Ok(DynamicLibrary { handle: handle })
56         }
57     }
58
59     /// Prepends a path to this process's search path for dynamic libraries
60     pub fn prepend_search_path(path: &Path) {
61         let mut search_path = DynamicLibrary::search_path();
62         search_path.insert(0, path.to_path_buf());
63         env::set_var(DynamicLibrary::envvar(), &DynamicLibrary::create_path(&search_path));
64     }
65
66     /// From a slice of paths, create a new vector which is suitable to be an
67     /// environment variable for this platforms dylib search path.
68     pub fn create_path(path: &[PathBuf]) -> OsString {
69         let mut newvar = OsString::new();
70         for (i, path) in path.iter().enumerate() {
71             if i > 0 { newvar.push(DynamicLibrary::separator()); }
72             newvar.push(path);
73         }
74         return newvar;
75     }
76
77     /// Returns the environment variable for this process's dynamic library
78     /// search path
79     pub fn envvar() -> &'static str {
80         if cfg!(windows) {
81             "PATH"
82         } else if cfg!(target_os = "macos") {
83             "DYLD_LIBRARY_PATH"
84         } else {
85             "LD_LIBRARY_PATH"
86         }
87     }
88
89     fn separator() -> &'static str {
90         if cfg!(windows) { ";" } else { ":" }
91     }
92
93     /// Returns the current search path for dynamic libraries being used by this
94     /// process
95     pub fn search_path() -> Vec<PathBuf> {
96         match env::var_os(DynamicLibrary::envvar()) {
97             Some(var) => env::split_paths(&var).collect(),
98             None => Vec::new(),
99         }
100     }
101
102     /// Accesses the value at the symbol of the dynamic library.
103     pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
104         // This function should have a lifetime constraint of 'a on
105         // T but that feature is still unimplemented
106
107         let raw_string = CString::new(symbol).unwrap();
108         let maybe_symbol_value = dl::check_for_errors_in(|| {
109             dl::symbol(self.handle, raw_string.as_ptr())
110         });
111
112         // The value must not be constructed if there is an error so
113         // the destructor does not run.
114         match maybe_symbol_value {
115             Err(err) => Err(err),
116             Ok(symbol_value) => Ok(mem::transmute(symbol_value))
117         }
118     }
119 }
120
121 #[cfg(all(test, not(target_os = "ios")))]
122 mod tests {
123     use super::*;
124     use prelude::v1::*;
125     use libc;
126     use mem;
127     use path::Path;
128
129     #[test]
130     #[cfg_attr(any(windows,
131                    target_os = "android",  // FIXME #10379
132                    target_env = "musl"), ignore)]
133     fn test_loading_cosine() {
134         // The math library does not need to be loaded since it is already
135         // statically linked in
136         let libm = match DynamicLibrary::open(None) {
137             Err(error) => panic!("Could not load self as module: {}", error),
138             Ok(libm) => libm
139         };
140
141         let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
142             match libm.symbol("cos") {
143                 Err(error) => panic!("Could not load function cos: {}", error),
144                 Ok(cosine) => mem::transmute::<*mut u8, _>(cosine)
145             }
146         };
147
148         let argument = 0.0;
149         let expected_result = 1.0;
150         let result = cosine(argument);
151         if result != expected_result {
152             panic!("cos({}) != {} but equaled {} instead", argument,
153                    expected_result, result)
154         }
155     }
156
157     #[test]
158     #[cfg(any(target_os = "linux",
159               target_os = "macos",
160               target_os = "freebsd",
161               target_os = "dragonfly",
162               target_os = "bitrig",
163               target_os = "netbsd",
164               target_os = "openbsd"))]
165     fn test_errors_do_not_crash() {
166         // Open /dev/null as a library to get an error, and make sure
167         // that only causes an error, and not a crash.
168         let path = Path::new("/dev/null");
169         match DynamicLibrary::open(Some(&path)) {
170             Err(_) => {}
171             Ok(_) => panic!("Successfully opened the empty library.")
172         }
173     }
174 }
175
176 #[cfg(any(target_os = "linux",
177           target_os = "android",
178           target_os = "macos",
179           target_os = "ios",
180           target_os = "freebsd",
181           target_os = "dragonfly",
182           target_os = "bitrig",
183           target_os = "netbsd",
184           target_os = "openbsd"))]
185 mod dl {
186     use prelude::v1::*;
187
188     use ffi::{CStr, OsStr};
189     use str;
190     use libc;
191     use ptr;
192
193     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
194         check_for_errors_in(|| {
195             unsafe {
196                 match filename {
197                     Some(filename) => open_external(filename),
198                     None => open_internal(),
199                 }
200             }
201         })
202     }
203
204     const LAZY: libc::c_int = 1;
205
206     unsafe fn open_external(filename: &OsStr) -> *mut u8 {
207         let s = filename.to_cstring().unwrap();
208         dlopen(s.as_ptr(), LAZY) as *mut u8
209     }
210
211     unsafe fn open_internal() -> *mut u8 {
212         dlopen(ptr::null(), LAZY) as *mut u8
213     }
214
215     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
216         F: FnOnce() -> T,
217     {
218         use sync::StaticMutex;
219         static LOCK: StaticMutex = StaticMutex::new();
220         unsafe {
221             // dlerror isn't thread safe, so we need to lock around this entire
222             // sequence
223             let _guard = LOCK.lock();
224             let _old_error = dlerror();
225
226             let result = f();
227
228             let last_error = dlerror() as *const _;
229             let ret = if ptr::null() == last_error {
230                 Ok(result)
231             } else {
232                 let s = CStr::from_ptr(last_error).to_bytes();
233                 Err(str::from_utf8(s).unwrap().to_string())
234             };
235
236             ret
237         }
238     }
239
240     pub unsafe fn symbol(handle: *mut u8,
241                          symbol: *const libc::c_char) -> *mut u8 {
242         dlsym(handle as *mut libc::c_void, symbol) as *mut u8
243     }
244     pub unsafe fn close(handle: *mut u8) {
245         dlclose(handle as *mut libc::c_void); ()
246     }
247
248     extern {
249         fn dlopen(filename: *const libc::c_char,
250                   flag: libc::c_int) -> *mut libc::c_void;
251         fn dlerror() -> *mut libc::c_char;
252         fn dlsym(handle: *mut libc::c_void,
253                  symbol: *const libc::c_char) -> *mut libc::c_void;
254         fn dlclose(handle: *mut libc::c_void) -> libc::c_int;
255     }
256 }
257
258 #[cfg(target_os = "windows")]
259 mod dl {
260     use prelude::v1::*;
261
262     use ffi::OsStr;
263     use libc;
264     use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED;
265     use sys::os;
266     use os::windows::prelude::*;
267     use ptr;
268     use sys::c::SetThreadErrorMode;
269
270     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
271         // disable "dll load failed" error dialog.
272         let mut use_thread_mode = true;
273         let prev_error_mode = unsafe {
274             // SEM_FAILCRITICALERRORS 0x01
275             let new_error_mode = 1;
276             let mut prev_error_mode = 0;
277             // Windows >= 7 supports thread error mode.
278             let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode);
279             if result == 0 {
280                 let err = os::errno();
281                 if err as libc::c_int == ERROR_CALL_NOT_IMPLEMENTED {
282                     use_thread_mode = false;
283                     // SetThreadErrorMode not found. use fallback solution:
284                     // SetErrorMode() Note that SetErrorMode is process-wide so
285                     // this can cause race condition!  However, since even
286                     // Windows APIs do not care of such problem (#20650), we
287                     // just assume SetErrorMode race is not a great deal.
288                     prev_error_mode = SetErrorMode(new_error_mode);
289                 }
290             }
291             prev_error_mode
292         };
293
294         unsafe {
295             SetLastError(0);
296         }
297
298         let result = match filename {
299             Some(filename) => {
300                 let filename_str: Vec<_> =
301                     filename.encode_wide().chain(Some(0)).collect();
302                 let result = unsafe {
303                     LoadLibraryW(filename_str.as_ptr() as *const libc::c_void)
304                 };
305                 // beware: Vec/String may change errno during drop!
306                 // so we get error here.
307                 if result == ptr::null_mut() {
308                     let errno = os::errno();
309                     Err(os::error_string(errno))
310                 } else {
311                     Ok(result as *mut u8)
312                 }
313             }
314             None => {
315                 let mut handle = ptr::null_mut();
316                 let succeeded = unsafe {
317                     GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle)
318                 };
319                 if succeeded == libc::FALSE {
320                     let errno = os::errno();
321                     Err(os::error_string(errno))
322                 } else {
323                     Ok(handle as *mut u8)
324                 }
325             }
326         };
327
328         unsafe {
329             if use_thread_mode {
330                 SetThreadErrorMode(prev_error_mode, ptr::null_mut());
331             } else {
332                 SetErrorMode(prev_error_mode);
333             }
334         }
335
336         result
337     }
338
339     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
340         F: FnOnce() -> T,
341     {
342         unsafe {
343             SetLastError(0);
344
345             let result = f();
346
347             let error = os::errno();
348             if 0 == error {
349                 Ok(result)
350             } else {
351                 Err(format!("Error code {}", error))
352             }
353         }
354     }
355
356     pub unsafe fn symbol(handle: *mut u8, symbol: *const libc::c_char) -> *mut u8 {
357         GetProcAddress(handle as *mut libc::c_void, symbol) as *mut u8
358     }
359     pub unsafe fn close(handle: *mut u8) {
360         FreeLibrary(handle as *mut libc::c_void); ()
361     }
362
363     #[allow(non_snake_case)]
364     extern "system" {
365         fn SetLastError(error: libc::size_t);
366         fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
367         fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16,
368                               handle: *mut *mut libc::c_void) -> libc::BOOL;
369         fn GetProcAddress(handle: *mut libc::c_void,
370                           name: *const libc::c_char) -> *mut libc::c_void;
371         fn FreeLibrary(handle: *mut libc::c_void);
372         fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
373     }
374 }