]> git.lizzy.rs Git - rust.git/blob - src/librustc_metadata/dynamic_lib.rs
Rollup merge of #64918 - GuillaumeGomez:long-err-explanation-E0551, 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
79 #[cfg(unix)]
80 mod dl {
81     use std::ffi::{CStr, OsStr, CString};
82     use std::os::unix::prelude::*;
83     use std::ptr;
84     use std::str;
85
86     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
87         check_for_errors_in(|| {
88             unsafe {
89                 match filename {
90                     Some(filename) => open_external(filename),
91                     None => open_internal(),
92                 }
93             }
94         })
95     }
96
97     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
98         check_for_errors_in(|| unsafe {
99             let s = CString::new(filename.as_bytes()).unwrap();
100             libc::dlopen(s.as_ptr(), libc::RTLD_GLOBAL | libc::RTLD_NOW) as *mut u8
101         })
102     }
103
104     unsafe fn open_external(filename: &OsStr) -> *mut u8 {
105         let s = CString::new(filename.as_bytes()).unwrap();
106         libc::dlopen(s.as_ptr(), libc::RTLD_LAZY) as *mut u8
107     }
108
109     unsafe fn open_internal() -> *mut u8 {
110         libc::dlopen(ptr::null(), libc::RTLD_LAZY) as *mut u8
111     }
112
113     pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where
114         F: FnOnce() -> T,
115     {
116         use std::sync::{Mutex, Once};
117         static INIT: Once = Once::new();
118         static mut LOCK: *mut Mutex<()> = ptr::null_mut();
119         unsafe {
120             INIT.call_once(|| {
121                 LOCK = Box::into_raw(Box::new(Mutex::new(())));
122             });
123             // dlerror isn't thread safe, so we need to lock around this entire
124             // sequence
125             let _guard = (*LOCK).lock();
126             let _old_error = libc::dlerror();
127
128             let result = f();
129
130             let last_error = libc::dlerror() as *const _;
131             let ret = if ptr::null() == last_error {
132                 Ok(result)
133             } else {
134                 let s = CStr::from_ptr(last_error).to_bytes();
135                 Err(str::from_utf8(s).unwrap().to_owned())
136             };
137
138             ret
139         }
140     }
141
142     pub unsafe fn symbol(handle: *mut u8,
143                          symbol: *const libc::c_char)
144                          -> Result<*mut u8, String> {
145         check_for_errors_in(|| {
146             libc::dlsym(handle as *mut libc::c_void, symbol) as *mut u8
147         })
148     }
149     pub unsafe fn close(handle: *mut u8) {
150         libc::dlclose(handle as *mut libc::c_void); ()
151     }
152 }
153
154 #[cfg(windows)]
155 mod dl {
156     use std::ffi::OsStr;
157     use std::io;
158     use std::os::windows::prelude::*;
159     use std::ptr;
160
161     use libc::{c_uint, c_void, c_char};
162
163     type DWORD = u32;
164     type HMODULE = *mut u8;
165     type BOOL = i32;
166     type LPCWSTR = *const u16;
167     type LPCSTR = *const i8;
168
169     extern "system" {
170         fn SetThreadErrorMode(dwNewMode: DWORD,
171                               lpOldMode: *mut DWORD) -> c_uint;
172         fn LoadLibraryW(name: LPCWSTR) -> HMODULE;
173         fn GetModuleHandleExW(dwFlags: DWORD,
174                               name: LPCWSTR,
175                               handle: *mut HMODULE) -> BOOL;
176         fn GetProcAddress(handle: HMODULE,
177                           name: LPCSTR) -> *mut c_void;
178         fn FreeLibrary(handle: HMODULE) -> BOOL;
179     }
180
181     pub fn open_global_now(filename: &OsStr) -> Result<*mut u8, String> {
182         open(Some(filename))
183     }
184
185     pub fn open(filename: Option<&OsStr>) -> Result<*mut u8, String> {
186         // disable "dll load failed" error dialog.
187         let prev_error_mode = unsafe {
188             // SEM_FAILCRITICALERRORS 0x01
189             let new_error_mode = 1;
190             let mut prev_error_mode = 0;
191             let result = SetThreadErrorMode(new_error_mode,
192                                             &mut prev_error_mode);
193             if result == 0 {
194                 return Err(io::Error::last_os_error().to_string())
195             }
196             prev_error_mode
197         };
198
199         let result = match filename {
200             Some(filename) => {
201                 let filename_str: Vec<_> =
202                     filename.encode_wide().chain(Some(0)).collect();
203                 let result = unsafe {
204                     LoadLibraryW(filename_str.as_ptr())
205                 };
206                 ptr_result(result)
207             }
208             None => {
209                 let mut handle = ptr::null_mut();
210                 let succeeded = unsafe {
211                     GetModuleHandleExW(0 as DWORD, ptr::null(), &mut handle)
212                 };
213                 if succeeded == 0 {
214                     Err(io::Error::last_os_error().to_string())
215                 } else {
216                     Ok(handle as *mut u8)
217                 }
218             }
219         };
220
221         unsafe {
222             SetThreadErrorMode(prev_error_mode, ptr::null_mut());
223         }
224
225         result
226     }
227
228     pub unsafe fn symbol(handle: *mut u8,
229                          symbol: *const c_char)
230                          -> Result<*mut u8, String> {
231         let ptr = GetProcAddress(handle as HMODULE, symbol) as *mut u8;
232         ptr_result(ptr)
233     }
234
235     pub unsafe fn close(handle: *mut u8) {
236         FreeLibrary(handle as HMODULE);
237     }
238
239     fn ptr_result<T>(ptr: *mut T) -> Result<*mut T, String> {
240         if ptr.is_null() {
241             Err(io::Error::last_os_error().to_string())
242         } else {
243             Ok(ptr)
244         }
245     }
246 }