#[cfg(windows)] extern crate windows_sys; extern crate libloading; use std::os::raw::c_void; use libloading::{Library, Symbol}; const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR"); const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR"); fn lib_path() -> std::path::PathBuf { [ TARGET_TMPDIR.unwrap_or(TARGET_DIR.unwrap_or("target")), "libtest_helpers.module", ] .iter() .collect() } fn make_helpers() { static ONCE: ::std::sync::Once = ::std::sync::Once::new(); ONCE.call_once(|| { let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); let mut cmd = ::std::process::Command::new(rustc); cmd.arg("src/test_helpers.rs").arg("-o").arg(lib_path()); if let Some(target) = std::env::var_os("TARGET") { cmd.arg("--target").arg(target); } else { eprintln!("WARNING: $TARGET NOT SPECIFIED! BUILDING HELPER MODULE FOR NATIVE TARGET."); } assert!(cmd .status() .expect("could not compile the test helpers!") .success()); }); } #[test] fn test_id_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); assert_eq!(42, f(42)); } } #[test] fn test_try_into_ptr() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol u32> = lib.get(b"test_identity_u32\0").unwrap(); let ptr: *mut c_void = f.try_as_raw_ptr().unwrap(); assert!(!ptr.is_null()); let ptr_casted : extern "C" fn(u32) -> u32 = std::mem::transmute(ptr); assert_eq!(42, ptr_casted(42)); } } #[repr(C)] #[derive(Clone, Copy, PartialEq, Debug)] struct S { a: u64, b: u32, c: u16, d: u8, } #[test] fn test_id_struct() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); assert_eq!( S { a: 1, b: 2, c: 3, d: 4 }, f(S { a: 1, b: 2, c: 3, d: 4 }) ); } } #[test] fn test_0_no_0() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let f: Symbol S> = lib.get(b"test_identity_struct\0").unwrap(); let f2: Symbol S> = lib.get(b"test_identity_struct").unwrap(); assert_eq!(*f, *f2); } } #[test] fn wrong_name_fails() { unsafe { Library::new("target/this_location_is_definitely_non existent:^~") .err() .unwrap(); } } #[test] fn missing_symbol_fails() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); } } #[test] fn interior_null_fails() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); lib.get::<*mut ()>(b"test\0_does_not_exist\0") .err() .unwrap(); } } #[test] fn test_incompatible_type() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); assert!(match lib.get::<()>(b"test_identity_u32\0") { Err(libloading::Error::IncompatibleSize) => true, _ => false, }) } } #[test] fn test_incompatible_type_named_fn() { make_helpers(); unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result, libloading::Error> { l.get::(b"test_identity_u32\0") } unsafe { let lib = Library::new(lib_path()).unwrap(); assert!(match get(&lib, test_incompatible_type_named_fn) { Err(libloading::Error::IncompatibleSize) => true, _ => false, }) } } #[test] fn test_static_u32() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); **var = 42; let help: Symbol u32> = lib.get(b"test_get_static_u32\0").unwrap(); assert_eq!(42, help()); } } #[test] fn test_static_ptr() { make_helpers(); unsafe { let lib = Library::new(lib_path()).unwrap(); let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); **var = *var as *mut _; let works: Symbol bool> = lib.get(b"test_check_static_ptr\0").unwrap(); assert!(works()); } } #[test] // Something about i686-pc-windows-gnu, makes dll initialisation code call abort when it is loaded // and unloaded many times. So far it seems like an issue with mingw, not libloading, so ignoring // the target. Especially since it is very unlikely to be fixed given the state of support its // support. #[cfg(not(all(target_arch = "x86", target_os = "windows", target_env = "gnu")))] fn manual_close_many_times() { make_helpers(); let join_handles: Vec<_> = (0..16) .map(|_| { std::thread::spawn(|| unsafe { for _ in 0..10000 { let lib = Library::new(lib_path()).expect("open library"); let _: Symbol u32> = lib.get(b"test_identity_u32").expect("get fn"); lib.close().expect("close is successful"); } }) }) .collect(); for handle in join_handles { handle.join().expect("thread should succeed"); } } #[cfg(unix)] #[test] fn library_this_get() { use libloading::os::unix::Library; make_helpers(); // SAFE: functions are never called unsafe { let _lib = Library::new(lib_path()).unwrap(); let this = Library::this(); // Library we loaded in `_lib` (should be RTLD_LOCAL). assert!(this .get::(b"test_identity_u32") .is_err()); // Something obscure from libc... assert!(this.get::(b"freopen").is_ok()); } } #[cfg(windows)] #[test] fn library_this() { use libloading::os::windows::Library; make_helpers(); unsafe { // SAFE: well-known library without initialisers is loaded. let _lib = Library::new(lib_path()).unwrap(); let this = Library::this().expect("this library"); // SAFE: functions are never called. // Library we loaded in `_lib`. assert!(this .get::(b"test_identity_u32") .is_err()); // Something "obscure" from kernel32... assert!(this.get::(b"GetLastError").is_err()); } } #[cfg(windows)] #[test] fn works_getlasterror() { use libloading::os::windows::{Library, Symbol}; use windows_sys::Win32::Foundation::{GetLastError, SetLastError}; unsafe { let lib = Library::new("kernel32.dll").unwrap(); let gle: Symbol u32> = lib.get(b"GetLastError").unwrap(); SetLastError(42); assert_eq!(GetLastError(), gle()) } } #[cfg(windows)] #[test] fn works_getlasterror0() { use libloading::os::windows::{Library, Symbol}; use windows_sys::Win32::Foundation::{GetLastError, SetLastError}; unsafe { let lib = Library::new("kernel32.dll").unwrap(); let gle: Symbol u32> = lib.get(b"GetLastError\0").unwrap(); SetLastError(42); assert_eq!(GetLastError(), gle()) } } #[cfg(windows)] #[test] fn library_open_already_loaded() { use libloading::os::windows::Library; // Present on Windows systems and NOT used by any other tests to prevent races. const LIBPATH: &str = "Msftedit.dll"; // Not loaded yet. assert!(match Library::open_already_loaded(LIBPATH) { Err(libloading::Error::GetModuleHandleExW { .. }) => true, _ => false, }); unsafe { let _lib = Library::new(LIBPATH).unwrap(); // Loaded now. assert!(Library::open_already_loaded(LIBPATH).is_ok()); } }