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