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