]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/dynamic_lib.rs
Rollup merge of #61409 - varkor:condition-trait-param-ice, r=oli-obk
[rust.git] / src / librustc_metadata / dynamic_lib.rs
1 //! Dynamic library facilities.
2 //!
3 //! A simple wrapper over the platform's dynamic library facilities
4
5 use std::ffi::CString;
6 use std::path::Path;
7
8 pub struct DynamicLibrary {
9     handle: *mut u8
10 }
11
12 impl Drop for DynamicLibrary {
13     fn drop(&mut self) {
14         unsafe {
15             dl::close(self.handle)
16         }
17     }
18 }
19
20 impl DynamicLibrary {
21     /// Lazily open a dynamic library. When passed None it gives a
22     /// handle to the calling process
23     pub fn open(filename: Option<&Path>) -> Result<DynamicLibrary, String> {
24         let maybe_library = dl::open(filename.map(|path| path.as_os_str()));
25
26         // The dynamic library must not be constructed if there is
27         // an error opening the library so the destructor does not
28         // run.
29         match maybe_library {
30             Err(err) => Err(err),
31             Ok(handle) => Ok(DynamicLibrary { handle })
32         }
33     }
34
35     /// Loads a dynamic library into the global namespace (RTLD_GLOBAL on Unix)
36     /// and do it now (don't use RTLD_LAZY on Unix).
37     pub fn open_global_now(filename: &Path) -> Result<DynamicLibrary, String> {
38         let maybe_library = dl::open_global_now(filename.as_os_str());
39         match maybe_library {
40             Err(err) => Err(err),
41             Ok(handle) => Ok(DynamicLibrary { handle })
42         }
43     }
44
45     /// Returns the environment variable for this process's dynamic library
46     /// search path
47     pub fn envvar() -> &'static str {
48         if cfg!(windows) {
49             "PATH"
50         } else if cfg!(target_os = "macos") {
51             "DYLD_LIBRARY_PATH"
52         } else if cfg!(target_os = "haiku") {
53             "LIBRARY_PATH"
54         } else {
55             "LD_LIBRARY_PATH"
56         }
57     }
58
59     /// Accesses the value at the symbol of the dynamic library.
60     pub unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String> {
61         // This function should have a lifetime constraint of 'a on
62         // T but that feature is still unimplemented
63
64         let raw_string = CString::new(symbol).unwrap();
65         let maybe_symbol_value = dl::symbol(self.handle, raw_string.as_ptr());
66
67         // The value must not be constructed if there is an error so
68         // the destructor does not run.
69         match maybe_symbol_value {
70             Err(err) => Err(err),
71             Ok(symbol_value) => Ok(symbol_value as *mut T)
72         }
73     }
74 }
75
76 #[cfg(test)]
77 mod tests {
78     use super::*;
79     use std::mem;
80
81     #[test]
82     fn test_loading_atoi() {
83         if cfg!(windows) {
84             return
85         }
86
87         // The C library does not need to be loaded since it is already linked in
88         let lib = match DynamicLibrary::open(None) {
89             Err(error) => panic!("Could not load self as module: {}", error),
90             Ok(lib) => lib
91         };
92
93         let atoi: extern fn(*const libc::c_char) -> libc::c_int = unsafe {
94             match lib.symbol("atoi") {
95                 Err(error) => panic!("Could not load function atoi: {}", error),
96                 Ok(atoi) => mem::transmute::<*mut u8, _>(atoi)
97             }
98         };
99
100         let argument = CString::new("1383428980").unwrap();
101         let expected_result = 0x52757374;
102         let result = atoi(argument.as_ptr());
103         if result != expected_result {
104             panic!("atoi({:?}) != {} but equaled {} instead", argument,
105                    expected_result, result)
106         }
107     }
108
109     #[test]
110     fn test_errors_do_not_crash() {
111         use std::path::Path;
112
113         if !cfg!(unix) {
114             return
115         }
116
117         // Open /dev/null as a library to get an error, and make sure
118         // that only causes an error, and not a crash.
119         let path = Path::new("/dev/null");
120         match DynamicLibrary::open(Some(&path)) {
121             Err(_) => {}
122             Ok(_) => panic!("Successfully opened the empty library.")
123         }
124     }
125 }
126
127 #[cfg(unix)]
128 mod dl {
129     use std::ffi::{CStr, OsStr, CString};
130     use std::os::unix::prelude::*;
131     use std::ptr;
132     use std::str;
133
134     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
135         check_for_errors_in(|| {
136             unsafe {
137                 match filename {
138                     Some(filename) => open_external(filename),
139                     None => open_internal(),
140                 }
141             }
142         })
143     }
144
145     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
146         check_for_errors_in(|| unsafe {
147             let s = CString::new(filename.as_bytes()).unwrap();
148             libc::dlopen(s.as_ptr(), libc::RTLD_GLOBAL | libc::RTLD_NOW) as *mut u8
149         })
150     }
151
152     unsafe fn open_external(filename: &OsStr) -> *mut u8 {
153         let s = CString::new(filename.as_bytes()).unwrap();
154         libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8
155     }
156
157     unsafe fn open_internal() -> *mut u8 {
158         libc::dlopen(ptr::null(), libc::RTLD_LAZY) as *mut u8
159     }
160
161     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
162         F: FnOnce() -> T,
163     {
164         use std::sync::{Mutex, Once, ONCE_INIT};
165         static INIT: Once = ONCE_INIT;
166         static mut LOCK: *mut Mutex<()> = 0 as *mut _;
167         unsafe {
168             INIT.call_once(|| {
169                 LOCK = Box::into_raw(Box::new(Mutex::new(())));
170             });
171             // dlerror isn't thread safe, so we need to lock around this entire
172             // sequence
173             let _guard = (*LOCK).lock();
174             let _old_error = libc::dlerror();
175
176             let result = f();
177
178             let last_error = libc::dlerror() as *const _;
179             let ret = if ptr::null() == last_error {
180                 Ok(result)
181             } else {
182                 let s = CStr::from_ptr(last_error).to_bytes();
183                 Err(str::from_utf8(s).unwrap().to_owned())
184             };
185
186             ret
187         }
188     }
189
190     pub unsafe fn symbol(handle: *mut u8,
191                          symbol: *const libc::c_char)
192                          -> Result<*mut u8, String> {
193         check_for_errors_in(|| {
194             libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8
195         })
196     }
197     pub unsafe fn close(handle: *mut u8) {
198         libc::dlclose(handle as *mut libc::c_void); ()
199     }
200 }
201
202 #[cfg(windows)]
203 mod dl {
204     use std::ffi::OsStr;
205     use std::io;
206     use std::os::windows::prelude::*;
207     use std::ptr;
208
209     use libc::{c_uint, c_void, c_char};
210
211     type DWORD = u32;
212     type HMODULE = *mut u8;
213     type BOOL = i32;
214     type LPCWSTR = *const u16;
215     type LPCSTR = *const i8;
216
217     extern "system" {
218         fn SetThreadErrorMode(dwNewMode: DWORD,
219                               lpOldMode: *mut DWORD) -> c_uint;
220         fn LoadLibraryW(name: LPCWSTR) -> HMODULE;
221         fn GetModuleHandleExW(dwFlags: DWORD,
222                               name: LPCWSTR,
223                               handle: *mut HMODULE) -> BOOL;
224         fn GetProcAddress(handle: HMODULE,
225                           name: LPCSTR) -> *mut c_void;
226         fn FreeLibrary(handle: HMODULE) -> BOOL;
227     }
228
229     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
230         open(Some(filename))
231     }
232
233     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
234         // disable "dll load failed" error dialog.
235         let prev_error_mode = unsafe {
236             // SEM_FAILCRITICALERRORS 0x01
237             let new_error_mode = 1;
238             let mut prev_error_mode = 0;
239             let result = SetThreadErrorMode(new_error_mode,
240                                             &mut prev_error_mode);
241             if result == 0 {
242                 return Err(io::Error::last_os_error().to_string())
243             }
244             prev_error_mode
245         };
246
247         let result = match filename {
248             Some(filename) => {
249                 let filename_str: Vec<_> =
250                     filename.encode_wide().chain(Some(0)).collect();
251                 let result = unsafe {
252                     LoadLibraryW(filename_str.as_ptr())
253                 };
254                 ptr_result(result)
255             }
256             None => {
257                 let mut handle = ptr::null_mut();
258                 let succeeded = unsafe {
259                     GetModuleHandleExW(0 as DWORD, ptr::null(), &mut handle)
260                 };
261                 if succeeded == 0 {
262                     Err(io::Error::last_os_error().to_string())
263                 } else {
264                     Ok(handle as *mut u8)
265                 }
266             }
267         };
268
269         unsafe {
270             SetThreadErrorMode(prev_error_mode, ptr::null_mut());
271         }
272
273         result
274     }
275
276     pub unsafe fn symbol(handle: *mut u8,
277                          symbol: *const c_char)
278                          -> Result<*mut u8, String> {
279         let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8;
280         ptr_result(ptr)
281     }
282
283     pub unsafe fn close(handle: *mut u8) {
284         FreeLibrary(handle as HMODULE);
285     }
286
287     fn ptr_result<T>(ptr: *mut T) -> Result<*mut T, String> {
288         if ptr.is_null() {
289             Err(io::Error::last_os_error().to_string())
290         } else {
291             Ok(ptr)
292         }
293     }
294 }