[go: up one dir, main page]

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