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