]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | ||
62682a34 SL |
15 | #![unstable(feature = "dynamic_lib", |
16 | reason = "API has not been scrutinized and is highly likely to \ | |
17 | either disappear or change")] | |
1a4d82fc JJ |
18 | #![allow(missing_docs)] |
19 | ||
20 | use prelude::v1::*; | |
21 | ||
85aaf69f | 22 | use env; |
c34b1796 AL |
23 | use ffi::{CString, OsString}; |
24 | use mem; | |
25 | use path::{Path, PathBuf}; | |
1a4d82fc | 26 | |
1a4d82fc JJ |
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 { | |
1a4d82fc JJ |
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> { | |
c34b1796 | 48 | let maybe_library = dl::open(filename.map(|path| path.as_os_str())); |
85aaf69f SL |
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 }) | |
1a4d82fc JJ |
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(); | |
c34b1796 AL |
62 | search_path.insert(0, path.to_path_buf()); |
63 | env::set_var(DynamicLibrary::envvar(), &DynamicLibrary::create_path(&search_path)); | |
1a4d82fc JJ |
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. | |
c34b1796 AL |
68 | pub fn create_path(path: &[PathBuf]) -> OsString { |
69 | let mut newvar = OsString::new(); | |
1a4d82fc JJ |
70 | for (i, path) in path.iter().enumerate() { |
71 | if i > 0 { newvar.push(DynamicLibrary::separator()); } | |
c34b1796 | 72 | newvar.push(path); |
1a4d82fc JJ |
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 | ||
c34b1796 AL |
89 | fn separator() -> &'static str { |
90 | if cfg!(windows) { ";" } else { ":" } | |
1a4d82fc JJ |
91 | } |
92 | ||
93 | /// Returns the current search path for dynamic libraries being used by this | |
94 | /// process | |
c34b1796 | 95 | pub fn search_path() -> Vec<PathBuf> { |
85aaf69f SL |
96 | match env::var_os(DynamicLibrary::envvar()) { |
97 | Some(var) => env::split_paths(&var).collect(), | |
98 | None => Vec::new(), | |
1a4d82fc | 99 | } |
1a4d82fc JJ |
100 | } |
101 | ||
9346a6ac | 102 | /// Accesses the value at the symbol of the dynamic library. |
1a4d82fc JJ |
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 | ||
85aaf69f | 107 | let raw_string = CString::new(symbol).unwrap(); |
1a4d82fc JJ |
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")))] | |
d9579d0f | 122 | mod tests { |
1a4d82fc JJ |
123 | use super::*; |
124 | use prelude::v1::*; | |
125 | use libc; | |
126 | use mem; | |
c34b1796 | 127 | use path::Path; |
1a4d82fc JJ |
128 | |
129 | #[test] | |
d9579d0f AL |
130 | #[cfg_attr(any(windows, |
131 | target_os = "android", // FIXME #10379 | |
132 | target_env = "musl"), ignore)] | |
1a4d82fc JJ |
133 | fn test_loading_cosine() { |
134 | // The math library does not need to be loaded since it is already | |
135 | // statically linked in | |
c34b1796 | 136 | let libm = match DynamicLibrary::open(None) { |
1a4d82fc JJ |
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", | |
85aaf69f | 161 | target_os = "dragonfly", |
c34b1796 | 162 | target_os = "bitrig", |
85aaf69f | 163 | target_os = "openbsd"))] |
1a4d82fc JJ |
164 | fn test_errors_do_not_crash() { |
165 | // Open /dev/null as a library to get an error, and make sure | |
166 | // that only causes an error, and not a crash. | |
167 | let path = Path::new("/dev/null"); | |
168 | match DynamicLibrary::open(Some(&path)) { | |
169 | Err(_) => {} | |
170 | Ok(_) => panic!("Successfully opened the empty library.") | |
171 | } | |
172 | } | |
173 | } | |
174 | ||
175 | #[cfg(any(target_os = "linux", | |
176 | target_os = "android", | |
177 | target_os = "macos", | |
178 | target_os = "ios", | |
179 | target_os = "freebsd", | |
85aaf69f | 180 | target_os = "dragonfly", |
c34b1796 | 181 | target_os = "bitrig", |
85aaf69f SL |
182 | target_os = "openbsd"))] |
183 | mod dl { | |
1a4d82fc JJ |
184 | use prelude::v1::*; |
185 | ||
c34b1796 | 186 | use ffi::{CStr, OsStr}; |
1a4d82fc JJ |
187 | use str; |
188 | use libc; | |
189 | use ptr; | |
190 | ||
c34b1796 | 191 | pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> { |
85aaf69f SL |
192 | check_for_errors_in(|| { |
193 | unsafe { | |
194 | match filename { | |
195 | Some(filename) => open_external(filename), | |
196 | None => open_internal(), | |
197 | } | |
198 | } | |
199 | }) | |
200 | } | |
201 | ||
202 | const LAZY: libc::c_int = 1; | |
203 | ||
c34b1796 AL |
204 | unsafe fn open_external(filename: &OsStr) -> *mut u8 { |
205 | let s = filename.to_cstring().unwrap(); | |
85aaf69f | 206 | dlopen(s.as_ptr(), LAZY) as *mut u8 |
1a4d82fc JJ |
207 | } |
208 | ||
85aaf69f SL |
209 | unsafe fn open_internal() -> *mut u8 { |
210 | dlopen(ptr::null(), LAZY) as *mut u8 | |
1a4d82fc JJ |
211 | } |
212 | ||
213 | pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where | |
214 | F: FnOnce() -> T, | |
215 | { | |
62682a34 SL |
216 | use sync::StaticMutex; |
217 | static LOCK: StaticMutex = StaticMutex::new(); | |
1a4d82fc JJ |
218 | unsafe { |
219 | // dlerror isn't thread safe, so we need to lock around this entire | |
220 | // sequence | |
221 | let _guard = LOCK.lock(); | |
222 | let _old_error = dlerror(); | |
223 | ||
224 | let result = f(); | |
225 | ||
226 | let last_error = dlerror() as *const _; | |
227 | let ret = if ptr::null() == last_error { | |
228 | Ok(result) | |
229 | } else { | |
85aaf69f | 230 | let s = CStr::from_ptr(last_error).to_bytes(); |
1a4d82fc JJ |
231 | Err(str::from_utf8(s).unwrap().to_string()) |
232 | }; | |
233 | ||
234 | ret | |
235 | } | |
236 | } | |
237 | ||
238 | pub unsafe fn symbol(handle: *mut u8, | |
239 | symbol: *const libc::c_char) -> *mut u8 { | |
240 | dlsym(handle as *mut libc::c_void, symbol) as *mut u8 | |
241 | } | |
242 | pub unsafe fn close(handle: *mut u8) { | |
243 | dlclose(handle as *mut libc::c_void); () | |
244 | } | |
245 | ||
1a4d82fc JJ |
246 | extern { |
247 | fn dlopen(filename: *const libc::c_char, | |
248 | flag: libc::c_int) -> *mut libc::c_void; | |
249 | fn dlerror() -> *mut libc::c_char; | |
250 | fn dlsym(handle: *mut libc::c_void, | |
251 | symbol: *const libc::c_char) -> *mut libc::c_void; | |
252 | fn dlclose(handle: *mut libc::c_void) -> libc::c_int; | |
253 | } | |
254 | } | |
255 | ||
256 | #[cfg(target_os = "windows")] | |
85aaf69f | 257 | mod dl { |
9346a6ac AL |
258 | use prelude::v1::*; |
259 | ||
c34b1796 | 260 | use ffi::OsStr; |
1a4d82fc | 261 | use libc; |
85aaf69f | 262 | use libc::consts::os::extra::ERROR_CALL_NOT_IMPLEMENTED; |
c34b1796 AL |
263 | use sys::os; |
264 | use os::windows::prelude::*; | |
1a4d82fc | 265 | use ptr; |
85aaf69f SL |
266 | use sys::c::compat::kernel32::SetThreadErrorMode; |
267 | ||
c34b1796 | 268 | pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> { |
85aaf69f SL |
269 | // disable "dll load failed" error dialog. |
270 | let mut use_thread_mode = true; | |
271 | let prev_error_mode = unsafe { | |
272 | // SEM_FAILCRITICALERRORS 0x01 | |
273 | let new_error_mode = 1; | |
274 | let mut prev_error_mode = 0; | |
275 | // Windows >= 7 supports thread error mode. | |
276 | let result = SetThreadErrorMode(new_error_mode, &mut prev_error_mode); | |
277 | if result == 0 { | |
278 | let err = os::errno(); | |
279 | if err as libc::c_int == ERROR_CALL_NOT_IMPLEMENTED { | |
280 | use_thread_mode = false; | |
c34b1796 AL |
281 | // SetThreadErrorMode not found. use fallback solution: |
282 | // SetErrorMode() Note that SetErrorMode is process-wide so | |
283 | // this can cause race condition! However, since even | |
284 | // Windows APIs do not care of such problem (#20650), we | |
285 | // just assume SetErrorMode race is not a great deal. | |
85aaf69f SL |
286 | prev_error_mode = SetErrorMode(new_error_mode); |
287 | } | |
288 | } | |
289 | prev_error_mode | |
290 | }; | |
1a4d82fc | 291 | |
85aaf69f SL |
292 | unsafe { |
293 | SetLastError(0); | |
294 | } | |
295 | ||
296 | let result = match filename { | |
297 | Some(filename) => { | |
c34b1796 | 298 | let filename_str: Vec<_> = |
62682a34 | 299 | filename.encode_wide().chain(Some(0)).collect(); |
85aaf69f SL |
300 | let result = unsafe { |
301 | LoadLibraryW(filename_str.as_ptr() as *const libc::c_void) | |
302 | }; | |
303 | // beware: Vec/String may change errno during drop! | |
304 | // so we get error here. | |
305 | if result == ptr::null_mut() { | |
306 | let errno = os::errno(); | |
307 | Err(os::error_string(errno)) | |
308 | } else { | |
309 | Ok(result as *mut u8) | |
310 | } | |
311 | } | |
312 | None => { | |
313 | let mut handle = ptr::null_mut(); | |
314 | let succeeded = unsafe { | |
315 | GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &mut handle) | |
316 | }; | |
317 | if succeeded == libc::FALSE { | |
318 | let errno = os::errno(); | |
319 | Err(os::error_string(errno)) | |
320 | } else { | |
321 | Ok(handle as *mut u8) | |
322 | } | |
323 | } | |
324 | }; | |
325 | ||
326 | unsafe { | |
327 | if use_thread_mode { | |
328 | SetThreadErrorMode(prev_error_mode, ptr::null_mut()); | |
329 | } else { | |
330 | SetErrorMode(prev_error_mode); | |
331 | } | |
332 | } | |
1a4d82fc | 333 | |
85aaf69f | 334 | result |
1a4d82fc JJ |
335 | } |
336 | ||
337 | pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where | |
338 | F: FnOnce() -> T, | |
339 | { | |
340 | unsafe { | |
341 | SetLastError(0); | |
342 | ||
343 | let result = f(); | |
344 | ||
345 | let error = os::errno(); | |
346 | if 0 == error { | |
347 | Ok(result) | |
348 | } else { | |
349 | Err(format!("Error code {}", error)) | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
354 | pub unsafe fn symbol(handle: *mut u8, symbol: *const libc::c_char) -> *mut u8 { | |
355 | GetProcAddress(handle as *mut libc::c_void, symbol) as *mut u8 | |
356 | } | |
357 | pub unsafe fn close(handle: *mut u8) { | |
358 | FreeLibrary(handle as *mut libc::c_void); () | |
359 | } | |
360 | ||
361 | #[allow(non_snake_case)] | |
362 | extern "system" { | |
363 | fn SetLastError(error: libc::size_t); | |
364 | fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void; | |
365 | fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *const u16, | |
85aaf69f | 366 | handle: *mut *mut libc::c_void) -> libc::BOOL; |
1a4d82fc JJ |
367 | fn GetProcAddress(handle: *mut libc::c_void, |
368 | name: *const libc::c_char) -> *mut libc::c_void; | |
369 | fn FreeLibrary(handle: *mut libc::c_void); | |
85aaf69f | 370 | fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint; |
1a4d82fc JJ |
371 | } |
372 | } |