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 #![allow(missing_docs)]
16 
17 use std::env;
18 use std::ffi::{CString, OsString};
19 use std::mem;
20 use std::path::{Path, PathBuf};
21 use libc;
22 
23 pub struct DynamicLibrary {
24     handle: *mut u8
25 }
26 
27 unsafe impl Send for DynamicLibrary {}
28 unsafe impl Sync for DynamicLibrary {}
29 
30 impl Drop for DynamicLibrary {
drop(&mut self)31     fn drop(&mut self) {
32         if let Err(str) = dl::check_for_errors_in(|| unsafe {
33             dl::close(self.handle)
34         }) {
35             panic!("{}", str)
36         }
37     }
38 }
39 
40 /// Special handles to be used with the `symbol_special` function. These are
41 /// provided by a GNU only extension and are not included as part of the POSIX
42 /// standard.
43 ///
44 /// See https://linux.die.net/man/3/dlsym for their behaviour.
45 #[cfg(target_os = "linux")]
46 pub enum SpecialHandles {
47     Next,
48     Default,
49 }
50 
51 impl DynamicLibrary {
52     // FIXME (#12938): Until DST lands, we cannot decompose &str into
53     // & and str, so we cannot usefully take ToCStr arguments by
54     // reference (without forcing an additional & around &str). So we
55     // are instead temporarily adding an instance for &Path, so that
56     // we can take ToCStr as owned. When DST lands, the &Path instance
57     // should be removed, and arguments bound by ToCStr should be
58     // passed by reference. (Here: in the `open` method.)
59 
60     /// Lazily loads the dynamic library named `filename` into memory and
61     /// then returns an opaque "handle" for that dynamic library.
62     ///
63     /// Returns a handle to the calling process when passed `None`.
open(filename: Option<&Path>) -> Result<Self, String>64     pub fn open(filename: Option<&Path>) -> Result<Self, String> {
65         // The dynamic library must not be constructed if there is
66         // an error opening the library so the destructor does not
67         // run.
68         dl::open(filename.map(|path| path.as_os_str()))
69             .map(|handle| DynamicLibrary { handle })
70     }
71 
72     /// Prepends a path to this process's search path for dynamic libraries
prepend_search_path(path: &Path)73     pub fn prepend_search_path(path: &Path) {
74         let mut search_path = Self::search_path();
75         search_path.insert(0, path.to_path_buf());
76         env::set_var(Self::envvar(), &Self::create_path(&search_path));
77     }
78 
79     /// From a slice of paths, create a new vector which is suitable to be an
80     /// environment variable for this platforms dylib search path.
create_path(path: &[PathBuf]) -> OsString81     pub fn create_path(path: &[PathBuf]) -> OsString {
82         let mut newvar = OsString::new();
83         for (i, path) in path.iter().enumerate() {
84             if i > 0 { newvar.push(Self::separator()); }
85             newvar.push(path);
86         }
87         newvar
88     }
89 
90     /// Returns the environment variable for this process's dynamic library
91     /// search path
envvar() -> &'static str92     pub fn envvar() -> &'static str {
93         if cfg!(windows) {
94             "PATH"
95         } else if cfg!(target_os = "macos") {
96             "DYLD_LIBRARY_PATH"
97         } else {
98             "LD_LIBRARY_PATH"
99         }
100     }
101 
102     //TODO: turn this and `envvar` into associated constants
separator() -> &'static str103     fn separator() -> &'static str {
104         if cfg!(windows) { ";" } else { ":" }
105     }
106 
107     /// Returns the current search path for dynamic libraries being used by this
108     /// process
search_path() -> Vec<PathBuf>109     pub fn search_path() -> Vec<PathBuf> {
110         match env::var_os(Self::envvar()) {
111             Some(var) => env::split_paths(&var).collect(),
112             None => Vec::new(),
113         }
114     }
115 
116     /// Returns the address of where symbol `symbol` was loaded into memory.
117     ///
118     /// In POSIX compliant systems, we return 'Err' if the symbol was not found,
119     /// in this library or any of the libraries that were automatically loaded
120     /// when this library was loaded.
symbol<T>(&self, symbol: &str) -> Result<*mut T, String>121     pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
122         // This function should have a lifetime constraint of 'a on
123         // T but that feature is still unimplemented
124 
125         let raw_string = CString::new(symbol).unwrap();
126         // The value must not be constructed if there is an error so
127         // the destructor does not run.
128         dl::check_for_errors_in(|| {
129                 dl::symbol(self.handle as *mut libc::c_void, raw_string.as_ptr() as *const _)
130             })
131             .map(|sym| mem::transmute(sym))
132     }
133 
134     /// Returns the address of the first occurance of symbol `symbol` using the
135     /// default library search order if you use `SpecialHandles::Default`.
136     ///
137     /// Returns the address of the next occurance of symbol `symbol` after the
138     /// current library in the default library search order if you use
139     /// `SpecialHandles::Next`.
140     #[cfg(target_os = "linux")]
symbol_special<T>(handle: SpecialHandles, symbol: &str) -> Result<*mut T, String>141     pub unsafe fn symbol_special<T>(handle: SpecialHandles, symbol: &str) -> Result<*mut T, String> {
142         // This function should have a lifetime constraint of 'a on
143         // T but that feature is still unimplemented
144 
145         let handle = match handle {
146             SpecialHandles::Next => mem::transmute::<libc::c_long, _>(-1),
147             SpecialHandles::Default => ::std::ptr::null_mut(),
148         };
149 
150         let raw_string = CString::new(symbol).unwrap();
151         // The value must not be constructed if there is an error so
152         // the destructor does not run.
153         dl::check_for_errors_in(|| {
154                 dl::symbol(handle, raw_string.as_ptr() as *const _)
155             })
156             .map(|sym| mem::transmute(sym))
157     }
158 }
159 
160 #[cfg(all(test, not(target_os = "ios")))]
161 mod test {
162     use super::*;
163     use std::mem;
164     use std::path::Path;
165 
166     #[test]
167     #[cfg_attr(any(windows, target_os = "android"), ignore)] // FIXME #8818, #10379
test_loading_cosine()168     fn test_loading_cosine() {
169         // The math library does not need to be loaded since it is already
170         // statically linked in
171         let libm = match DynamicLibrary::open(None) {
172             Err(error) => panic!("Could not load self as module: {}", error),
173             Ok(libm) => libm
174         };
175 
176         let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
177             match libm.symbol("cos") {
178                 Err(error) => panic!("Could not load function cos: {}", error),
179                 Ok(cosine) => mem::transmute::<*mut u8, _>(cosine)
180             }
181         };
182 
183         let argument = 0.0;
184         let expected_result = 1.0;
185         let result = cosine(argument);
186         if result != expected_result {
187             panic!("cos({}) != {} but equaled {} instead", argument,
188                    expected_result, result)
189         }
190     }
191 
192     #[test]
193     #[cfg(any(target_os = "linux",
194               target_os = "macos",
195               target_os = "freebsd",
196               target_os = "fuchsia",
197               target_os = "netbsd",
198               target_os = "dragonfly",
199               target_os = "bitrig",
200               target_os = "openbsd",
201               target_os = "solaris"))]
test_errors_do_not_crash()202     fn test_errors_do_not_crash() {
203         // Open /dev/null as a library to get an error, and make sure
204         // that only causes an error, and not a crash.
205         let path = Path::new("/dev/null");
206         match DynamicLibrary::open(Some(&path)) {
207             Err(_) => {}
208             Ok(_) => panic!("Successfully opened the empty library.")
209         }
210     }
211 }
212 
213 //TODO: use `unix` shortcut?
214 #[cfg(any(target_os = "linux",
215           target_os = "android",
216           target_os = "macos",
217           target_os = "ios",
218           target_os = "fuchsia",
219           target_os = "freebsd",
220           target_os = "netbsd",
221           target_os = "dragonfly",
222           target_os = "bitrig",
223           target_os = "openbsd",
224           target_os = "solaris",
225           target_os = "emscripten"))]
226 mod dl {
227     use std::ffi::{CString, CStr, OsStr};
228     use std::os::unix::ffi::OsStrExt;
229     use std::str;
230     use libc;
231     use std::ptr;
232     use std::sync::Mutex;
233 
234     lazy_static! {
235         static ref LOCK: Mutex<()> = Mutex::new(());
236     }
237 
open(filename: Option<&OsStr>) -> Result<*mut u8, String>238     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
239         check_for_errors_in(|| unsafe {
240             match filename {
241                 Some(filename) => open_external(filename),
242                 None => open_internal(),
243             }
244         })
245     }
246 
247     const LAZY: libc::c_int = 1;
248 
open_external(filename: &OsStr) -> *mut u8249     unsafe fn open_external(filename: &OsStr) -> *mut u8 {
250         let s = CString::new(filename.as_bytes().to_vec()).unwrap();
251         dlopen(s.as_ptr() as *const _, LAZY) as *mut u8
252     }
253 
open_internal() -> *mut u8254     unsafe fn open_internal() -> *mut u8 {
255         dlopen(ptr::null(), LAZY) as *mut u8
256     }
257 
check_for_errors_in<T, F>(f: F) -> Result<T, String> where F: FnOnce() -> T,258     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
259         F: FnOnce() -> T,
260     {
261         unsafe {
262             // dlerror isn't thread safe, so we need to lock around this entire
263             // sequence
264             let _guard = LOCK.lock();
265             let _old_error = dlerror();
266 
267             let result = f();
268 
269             let last_error = dlerror() as *const _;
270             let ret = if ptr::null() == last_error {
271                 Ok(result)
272             } else {
273                 let s = CStr::from_ptr(last_error).to_bytes();
274                 Err(str::from_utf8(s).unwrap().to_string())
275             };
276 
277             ret
278         }
279     }
280 
symbol( handle: *mut libc::c_void, symbol: *const libc::c_char, ) -> *mut u8281     pub unsafe fn symbol(
282         handle: *mut libc::c_void,
283         symbol: *const libc::c_char,
284     ) -> *mut u8 {
285         dlsym(handle, symbol) as *mut u8
286     }
287 
close(handle: *mut u8)288     pub unsafe fn close(handle: *mut u8) {
289         dlclose(handle as *mut libc::c_void); ()
290     }
291 
292     extern {
dlopen( filename: *const libc::c_char, flag: libc::c_int, ) -> *mut libc::c_void293         fn dlopen(
294             filename: *const libc::c_char,
295             flag: libc::c_int,
296         ) -> *mut libc::c_void;
dlerror() -> *mut libc::c_char297         fn dlerror() -> *mut libc::c_char;
dlsym( handle: *mut libc::c_void, symbol: *const libc::c_char, ) -> *mut libc::c_void298         fn dlsym(
299             handle: *mut libc::c_void,
300             symbol: *const libc::c_char,
301         ) -> *mut libc::c_void;
dlclose( handle: *mut libc::c_void, ) -> libc::c_int302         fn dlclose(
303             handle: *mut libc::c_void,
304         ) -> libc::c_int;
305     }
306 }
307 
308 #[cfg(target_os = "windows")]
309 mod dl {
310     use std::ffi::OsStr;
311     use std::iter::Iterator;
312     use libc;
313     use std::ops::FnOnce;
314     use std::io::Error as IoError;
315     use std::os::windows::prelude::*;
316     use std::option::Option::{self, Some, None};
317     use std::ptr;
318     use std::result::Result;
319     use std::result::Result::{Ok, Err};
320     use std::string::String;
321     use std::vec::Vec;
322 
open(filename: Option<&OsStr>) -> Result<*mut u8, String>323     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
324         // disable "dll load failed" error dialog.
325         let prev_error_mode = unsafe {
326             // SEM_FAILCRITICALERRORS 0x01
327             let new_error_mode = 1;
328             SetErrorMode(new_error_mode)
329         };
330 
331         unsafe {
332             SetLastError(0);
333         }
334 
335         let result = match filename {
336             Some(filename) => {
337                 let filename_str: Vec<_> =
338                     filename.encode_wide().chain(Some(0).into_iter()).collect();
339                 let result = unsafe {
340                     LoadLibraryW(filename_str.as_ptr() as *const libc::c_void)
341                 };
342                 // beware: Vec/String may change errno during drop!
343                 // so we get error here.
344                 if result == ptr::null_mut() {
345                     Err(format!("{}", IoError::last_os_error()))
346                 } else {
347                     Ok(result as *mut u8)
348                 }
349             }
350             None => {
351                 let mut handle = ptr::null_mut();
352                 let succeeded = unsafe {
353                     GetModuleHandleExW(0, ptr::null(), &mut handle)
354                 };
355                 if succeeded == 0 {
356                     Err(format!("{}", IoError::last_os_error()))
357                 } else {
358                     Ok(handle as *mut u8)
359                 }
360             }
361         };
362 
363         unsafe {
364             SetErrorMode(prev_error_mode);
365         }
366 
367         result
368     }
369 
check_for_errors_in<T, F>(f: F) -> Result<T, String> where F: FnOnce() -> T,370     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
371         F: FnOnce() -> T,
372     {
373         unsafe {
374             SetLastError(0);
375 
376             let result = f();
377 
378             let error = IoError::last_os_error();
379             if 0 == error.raw_os_error().unwrap() {
380                 Ok(result)
381             } else {
382                 Err(format!("{}", error))
383             }
384         }
385     }
386 
symbol(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut u8387     pub unsafe fn symbol(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut u8 {
388         GetProcAddress(handle, symbol) as *mut u8
389     }
close(handle: *mut u8)390     pub unsafe fn close(handle: *mut u8) {
391         FreeLibrary(handle as *mut libc::c_void); ()
392     }
393 
394     #[allow(non_snake_case)]
395     extern "system" {
SetLastError(error: libc::size_t)396         fn SetLastError(error: libc::size_t);
LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void397         fn LoadLibraryW(name: *const libc::c_void) -> *mut libc::c_void;
GetModuleHandleExW( dwFlags: u32, name: *const u16, handle: *mut *mut libc::c_void, ) -> i32398         fn GetModuleHandleExW(
399             dwFlags: u32,
400             name: *const u16,
401             handle: *mut *mut libc::c_void,
402         ) -> i32;
GetProcAddress( handle: *mut libc::c_void, name: *const libc::c_char, ) -> *mut libc::c_void403         fn GetProcAddress(
404             handle: *mut libc::c_void,
405             name: *const libc::c_char,
406         ) -> *mut libc::c_void;
FreeLibrary(handle: *mut libc::c_void)407         fn FreeLibrary(handle: *mut libc::c_void);
SetErrorMode(uMode: libc::c_uint) -> libc::c_uint408         fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint;
409     }
410 }
411