]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/dynamic_lib.rs
Auto merge of #57195 - czipperz:mention_ToString_in_std_fmt_docs, r=SimonSapin
[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     /// Load 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 libc;
80     use std::mem;
81
82     #[test]
83     fn test_loading_atoi() {
84         if cfg!(windows) {
85             return
86         }
87
88         // The C library does not need to be loaded since it is already linked in
89         let lib = match DynamicLibrary::open(None) {
90             Err(error) => panic!("Could not load self as module: {}", error),
91             Ok(lib) => lib
92         };
93
94         let atoi: extern fn(*const libc::c_char) -> libc::c_int = unsafe {
95             match lib.symbol("atoi") {
96                 Err(error) => panic!("Could not load function atoi: {}", error),
97                 Ok(atoi) => mem::transmute::<*mut u8, _>(atoi)
98             }
99         };
100
101         let argument = CString::new("1383428980").unwrap();
102         let expected_result = 0x52757374;
103         let result = atoi(argument.as_ptr());
104         if result != expected_result {
105             panic!("atoi({:?}) != {} but equaled {} instead", argument,
106                    expected_result, result)
107         }
108     }
109
110     #[test]
111     fn test_errors_do_not_crash() {
112         use std::path::Path;
113
114         if !cfg!(unix) {
115             return
116         }
117
118         // Open /dev/null as a library to get an error, and make sure
119         // that only causes an error, and not a crash.
120         let path = Path::new("/dev/null");
121         match DynamicLibrary::open(Some(&path)) {
122             Err(_) => {}
123             Ok(_) => panic!("Successfully opened the empty library.")
124         }
125     }
126 }
127
128 #[cfg(unix)]
129 mod dl {
130     use libc;
131     use std::ffi::{CStr, OsStr, CString};
132     use std::os::unix::prelude::*;
133     use std::ptr;
134     use std::str;
135
136     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
137         check_for_errors_in(|| {
138             unsafe {
139                 match filename {
140                     Some(filename) => open_external(filename),
141                     None => open_internal(),
142                 }
143             }
144         })
145     }
146
147     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
148         check_for_errors_in(|| unsafe {
149             let s = CString::new(filename.as_bytes()).unwrap();
150             libc::dlopen(s.as_ptr(), libc::RTLD_GLOBAL | libc::RTLD_NOW) as *mut u8
151         })
152     }
153
154     unsafe fn open_external(filename: &OsStr) -> *mut u8 {
155         let s = CString::new(filename.as_bytes()).unwrap();
156         libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8
157     }
158
159     unsafe fn open_internal() -> *mut u8 {
160         libc::dlopen(ptr::null(), libc::RTLD_LAZY) as *mut u8
161     }
162
163     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
164         F: FnOnce() -> T,
165     {
166         use std::sync::{Mutex, Once, ONCE_INIT};
167         static INIT: Once = ONCE_INIT;
168         static mut LOCK: *mut Mutex<()> = 0 as *mut _;
169         unsafe {
170             INIT.call_once(|| {
171                 LOCK = Box::into_raw(Box::new(Mutex::new(())));
172             });
173             // dlerror isn't thread safe, so we need to lock around this entire
174             // sequence
175             let _guard = (*LOCK).lock();
176             let _old_error = libc::dlerror();
177
178             let result = f();
179
180             let last_error = libc::dlerror() as *const _;
181             let ret = if ptr::null() == last_error {
182                 Ok(result)
183             } else {
184                 let s = CStr::from_ptr(last_error).to_bytes();
185                 Err(str::from_utf8(s).unwrap().to_owned())
186             };
187
188             ret
189         }
190     }
191
192     pub unsafe fn symbol(handle: *mut u8,
193                          symbol: *const libc::c_char)
194                          -> Result<*mut u8, String> {
195         check_for_errors_in(|| {
196             libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8
197         })
198     }
199     pub unsafe fn close(handle: *mut u8) {
200         libc::dlclose(handle as *mut libc::c_void); ()
201     }
202 }
203
204 #[cfg(windows)]
205 mod dl {
206     use std::ffi::OsStr;
207     use std::io;
208     use std::os::windows::prelude::*;
209     use std::ptr;
210
211     use libc::{c_uint, c_void, c_char};
212
213     type DWORD = u32;
214     type HMODULE = *mut u8;
215     type BOOL = i32;
216     type LPCWSTR = *const u16;
217     type LPCSTR = *const i8;
218
219     extern "system" {
220         fn SetThreadErrorMode(dwNewMode: DWORD,
221                               lpOldMode: *mut DWORD) -> c_uint;
222         fn LoadLibraryW(name: LPCWSTR) -> HMODULE;
223         fn GetModuleHandleExW(dwFlags: DWORD,
224                               name: LPCWSTR,
225                               handle: *mut HMODULE) -> BOOL;
226         fn GetProcAddress(handle: HMODULE,
227                           name: LPCSTR) -> *mut c_void;
228         fn FreeLibrary(handle: HMODULE) -> BOOL;
229     }
230
231     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
232         open(Some(filename))
233     }
234
235     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
236         // disable "dll load failed" error dialog.
237         let prev_error_mode = unsafe {
238             // SEM_FAILCRITICALERRORS 0x01
239             let new_error_mode = 1;
240             let mut prev_error_mode = 0;
241             let result = SetThreadErrorMode(new_error_mode,
242                                             &mut prev_error_mode);
243             if result == 0 {
244                 return Err(io::Error::last_os_error().to_string())
245             }
246             prev_error_mode
247         };
248
249         let result = match filename {
250             Some(filename) => {
251                 let filename_str: Vec<_> =
252                     filename.encode_wide().chain(Some(0)).collect();
253                 let result = unsafe {
254                     LoadLibraryW(filename_str.as_ptr())
255                 };
256                 ptr_result(result)
257             }
258             None => {
259                 let mut handle = ptr::null_mut();
260                 let succeeded = unsafe {
261                     GetModuleHandleExW(0 as DWORD, ptr::null(), &mut handle)
262                 };
263                 if succeeded == 0 {
264                     Err(io::Error::last_os_error().to_string())
265                 } else {
266                     Ok(handle as *mut u8)
267                 }
268             }
269         };
270
271         unsafe {
272             SetThreadErrorMode(prev_error_mode, ptr::null_mut());
273         }
274
275         result
276     }
277
278     pub unsafe fn symbol(handle: *mut u8,
279                          symbol: *const c_char)
280                          -> Result<*mut u8, String> {
281         let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8;
282         ptr_result(ptr)
283     }
284
285     pub unsafe fn close(handle: *mut u8) {
286         FreeLibrary(handle as HMODULE);
287     }
288
289     fn ptr_result<T>(ptr: *mut T) -> Result<*mut T, String> {
290         if ptr.is_null() {
291             Err(io::Error::last_os_error().to_string())
292         } else {
293             Ok(ptr)
294         }
295     }
296 }