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