(index<- )        ./libstd/unstable/dynamic_lib.rs

    git branch:    * master           5200215 auto merge of #14035 : alexcrichton/rust/experimental, r=huonw
    modified:    Fri May  9 13:02:28 2014
   1  // Copyright 2013 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  /*!
  12  
  13  Dynamic library facilities.
  14  
  15  A simple wrapper over the platform's dynamic library facilities
  16  
  17  */
  18  
  19  use c_str::ToCStr;
  20  use cast;
  21  use iter::Iterator;
  22  use ops::*;
  23  use option::*;
  24  use os;
  25  use path::GenericPath;
  26  use path;
  27  use result::*;
  28  use slice::{Vector,OwnedVector};
  29  use str;
  30  use vec::Vec;
  31  
  32  pub struct DynamicLibrary { handle: *u8}
  33  
  34  impl Drop for DynamicLibrary {
  35      fn drop(&mut self) {
  36          match dl::check_for_errors_in(|| {
  37              unsafe {
  38                  dl::close(self.handle)
  39              }
  40          }) {
  41              Ok(()) => {},
  42              Err(str) => fail!("{}", str)
  43          }
  44      }
  45  }
  46  
  47  impl DynamicLibrary {
  48      /// Lazily open a dynamic library. When passed None it gives a
  49      /// handle to the calling process
  50      pub fn open(filenameOption<&path::Path>) -> Result<DynamicLibrary, ~str> {
  51          unsafe {
  52              let maybe_library = dl::check_for_errors_in(|| {
  53                  match filename {
  54                      Some(name) => dl::open_external(name),
  55                      None => dl::open_internal()
  56                  }
  57              });
  58  
  59              // The dynamic library must not be constructed if there is
  60              // an error opening the library so the destructor does not
  61              // run.
  62              match maybe_library {
  63                  Err(err) => Err(err),
  64                  Ok(handle) => Ok(DynamicLibrary { handle: handle })
  65              }
  66          }
  67      }
  68  
  69      /// Appends a path to the system search path for dynamic libraries
  70      pub fn add_search_path(path&path::Path) {
  71          let (envvar, sep) = if cfg!(windows) {
  72              ("PATH", ';' as u8)
  73          } else if cfg!(target_os = "macos") {
  74              ("DYLD_LIBRARY_PATH", ':' as u8)
  75          } else {
  76              ("LD_LIBRARY_PATH", ':' as u8)
  77          };
  78          let newenv = os::getenv_as_bytes(envvar).unwrap_or(box []);
  79          let mut newenv = newenv.move_iter().collect::<Vec<_>>();
  80          newenv.push_all(&[sep]);
  81          newenv.push_all(path.as_vec());
  82          os::setenv(envvar, str::from_utf8(newenv.as_slice()).unwrap());
  83      }
  84  
  85      /// Access the value at the symbol of the dynamic library
  86      pub unsafe fn symbol<T>(&self, symbol&str) -> Result<T, ~str> {
  87          // This function should have a lifetime constraint of 'a on
  88          // T but that feature is still unimplemented
  89  
  90          let maybe_symbol_value = dl::check_for_errors_in(|| {
  91              symbol.with_c_str(|raw_string| {
  92                  dl::symbol(self.handle, raw_string)
  93              })
  94          });
  95  
  96          // The value must not be constructed if there is an error so
  97          // the destructor does not run.
  98          match maybe_symbol_value {
  99              Err(err) => Err(err),
 100              Ok(symbol_value) => Ok(cast::transmute(symbol_value))
 101          }
 102      }
 103  }
 104  
 105  #[cfg(test)]
 106  mod test {
 107      use super::*;
 108      use prelude::*;
 109      use libc;
 110  
 111      #[test]
 112      #[ignore(cfg(windows))] // FIXME #8818
 113      #[ignore(cfg(target_os="android"))] // FIXME(#10379)
 114      fn test_loading_cosine() {
 115          // The math library does not need to be loaded since it is already
 116          // statically linked in
 117          let libm = match DynamicLibrary::open(None) {
 118              Err(error) => fail!("Could not load self as module: {}", error),
 119              Ok(libm) => libm
 120          };
 121  
 122          let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
 123              match libm.symbol("cos") {
 124                  Err(error) => fail!("Could not load function cos: {}", error),
 125                  Ok(cosine) => cosine
 126              }
 127          };
 128  
 129          let argument = 0.0;
 130          let expected_result = 1.0;
 131          let result = cosine(argument);
 132          if result != expected_result {
 133              fail!("cos({:?}) != {:?} but equaled {:?} instead", argument,
 134                     expected_result, result)
 135          }
 136      }
 137  
 138      #[test]
 139      #[cfg(target_os = "linux")]
 140      #[cfg(target_os = "macos")]
 141      #[cfg(target_os = "freebsd")]
 142      fn test_errors_do_not_crash() {
 143          // Open /dev/null as a library to get an error, and make sure
 144          // that only causes an error, and not a crash.
 145          let path = GenericPath::new("/dev/null");
 146          match DynamicLibrary::open(Some(&path)) {
 147              Err(_) => {}
 148              Ok(_) => fail!("Successfully opened the empty library.")
 149          }
 150      }
 151  }
 152  
 153  #[cfg(target_os = "linux")]
 154  #[cfg(target_os = "android")]
 155  #[cfg(target_os = "macos")]
 156  #[cfg(target_os = "freebsd")]
 157  pub mod dl {
 158      use c_str::ToCStr;
 159      use libc;
 160      use path;
 161      use ptr;
 162      use str;
 163      use result::*;
 164  
 165      pub unsafe fn open_external(filename: &path::Path) -> *u8 {
 166          filename.with_c_str(|raw_name| {
 167              dlopen(raw_name, Lazy as libc::c_int) as *u8
 168          })
 169      }
 170  
 171      pub unsafe fn open_internal() -> *u8 {
 172          dlopen(ptr::null(), Lazy as libc::c_int) as *u8
 173      }
 174  
 175      pub fn check_for_errors_in<T>(f: || -> T) -> Result<T, ~str> {
 176          use unstable::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
 177          static mut lock: StaticNativeMutex = NATIVE_MUTEX_INIT;
 178          unsafe {
 179              // dlerror isn't thread safe, so we need to lock around this entire
 180              // sequence
 181              let _guard = lock.lock();
 182              let _old_error = dlerror();
 183  
 184              let result = f();
 185  
 186              let last_error = dlerror();
 187              let ret = if ptr::null() == last_error {
 188                  Ok(result)
 189              } else {
 190                  Err(str::raw::from_c_str(last_error))
 191              };
 192  
 193              ret
 194          }
 195      }
 196  
 197      pub unsafe fn symbol(handle: *u8, symbol: *libc::c_char) -> *u8 {
 198          dlsym(handle as *libc::c_void, symbol) as *u8
 199      }
 200      pub unsafe fn close(handle: *u8) {
 201          dlclose(handle as *libc::c_void); ()
 202      }
 203  
 204      pub enum RTLD {
 205          Lazy = 1,
 206          Now = 2,
 207          Global = 256,
 208          Local = 0,
 209      }
 210  
 211      #[link_name = "dl"]
 212      extern {
 213          fn dlopen(filename: *libc::c_char, flaglibc::c_int) -> *libc::c_void;
 214          fn dlerror() -> *libc::c_char;
 215          fn dlsym(handle: *libc::c_void, symbol: *libc::c_char) -> *libc::c_void;
 216          fn dlclose(handle: *libc::c_void) -> libc::c_int;
 217      }
 218  }
 219  
 220  #[cfg(target_os = "win32")]
 221  pub mod dl {
 222      use libc;
 223      use os;
 224      use path::GenericPath;
 225      use path;
 226      use ptr;
 227      use result::{Ok, Err, Result};
 228  
 229      pub unsafe fn open_external(filename: &path::Path) -> *u8 {
 230          os::win32::as_utf16_p(filename.as_str().unwrap(), |raw_name| {
 231              LoadLibraryW(raw_name as *libc::c_void) as *u8
 232          })
 233      }
 234  
 235      pub unsafe fn open_internal() -> *u8 {
 236          let handle = ptr::null();
 237          GetModuleHandleExW(0 as libc::DWORD, ptr::null(), &handle as **libc::c_void);
 238          handle as *u8
 239      }
 240  
 241      pub fn check_for_errors_in<T>(f: || -> T) -> Result<T, ~str> {
 242          unsafe {
 243              SetLastError(0);
 244  
 245              let result = f();
 246  
 247              let error = os::errno();
 248              if 0 == error {
 249                  Ok(result)
 250              } else {
 251                  Err(format!("Error code {}", error))
 252              }
 253          }
 254      }
 255  
 256      pub unsafe fn symbol(handle: *u8, symbol: *libc::c_char) -> *u8 {
 257          GetProcAddress(handle as *libc::c_void, symbol) as *u8
 258      }
 259      pub unsafe fn close(handle: *u8) {
 260          FreeLibrary(handle as *libc::c_void); ()
 261      }
 262  
 263      extern "system" {
 264          fn SetLastError(error: libc::size_t);
 265          fn LoadLibraryW(name: *libc::c_void) -> *libc::c_void;
 266          fn GetModuleHandleExW(dwFlags: libc::DWORD, name: *u16,
 267                                handle: **libc::c_void) -> *libc::c_void;
 268          fn GetProcAddress(handle: *libc::c_void, name: *libc::c_char) -> *libc::c_void;
 269          fn FreeLibrary(handle: *libc::c_void);
 270      }
 271  }