1#[cfg(windows)] 2extern crate winapi; 3 4extern crate libloading; 5use libloading::{Library, Symbol}; 6 7const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR"); 8const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR"); 9 10fn lib_path() -> std::path::PathBuf { 11 [ 12 TARGET_TMPDIR.unwrap_or(TARGET_DIR.unwrap_or("target")), 13 "libtest_helpers.module", 14 ] 15 .iter() 16 .collect() 17} 18 19fn make_helpers() { 20 static ONCE: ::std::sync::Once = ::std::sync::Once::new(); 21 ONCE.call_once(|| { 22 let rustc = std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into()); 23 let mut cmd = ::std::process::Command::new(rustc); 24 cmd.arg("src/test_helpers.rs").arg("-o").arg(lib_path()); 25 if let Some(target) = std::env::var_os("TARGET") { 26 cmd.arg("--target").arg(target); 27 } else { 28 eprintln!("WARNING: $TARGET NOT SPECIFIED! BUILDING HELPER MODULE FOR NATIVE TARGET."); 29 } 30 assert!(cmd 31 .status() 32 .expect("could not compile the test helpers!") 33 .success()); 34 }); 35} 36 37#[test] 38fn test_id_u32() { 39 make_helpers(); 40 unsafe { 41 let lib = Library::new(lib_path()).unwrap(); 42 let f: Symbol<unsafe extern "C" fn(u32) -> u32> = lib.get(b"test_identity_u32\0").unwrap(); 43 assert_eq!(42, f(42)); 44 } 45} 46 47#[repr(C)] 48#[derive(Clone, Copy, PartialEq, Debug)] 49struct S { 50 a: u64, 51 b: u32, 52 c: u16, 53 d: u8, 54} 55 56#[test] 57fn test_id_struct() { 58 make_helpers(); 59 unsafe { 60 let lib = Library::new(lib_path()).unwrap(); 61 let f: Symbol<unsafe extern "C" fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap(); 62 assert_eq!( 63 S { 64 a: 1, 65 b: 2, 66 c: 3, 67 d: 4 68 }, 69 f(S { 70 a: 1, 71 b: 2, 72 c: 3, 73 d: 4 74 }) 75 ); 76 } 77} 78 79#[test] 80fn test_0_no_0() { 81 make_helpers(); 82 unsafe { 83 let lib = Library::new(lib_path()).unwrap(); 84 let f: Symbol<unsafe extern "C" fn(S) -> S> = lib.get(b"test_identity_struct\0").unwrap(); 85 let f2: Symbol<unsafe extern "C" fn(S) -> S> = lib.get(b"test_identity_struct").unwrap(); 86 assert_eq!(*f, *f2); 87 } 88} 89 90#[test] 91fn wrong_name_fails() { 92 unsafe { 93 Library::new("target/this_location_is_definitely_non existent:^~") 94 .err() 95 .unwrap(); 96 } 97} 98 99#[test] 100fn missing_symbol_fails() { 101 make_helpers(); 102 unsafe { 103 let lib = Library::new(lib_path()).unwrap(); 104 lib.get::<*mut ()>(b"test_does_not_exist").err().unwrap(); 105 lib.get::<*mut ()>(b"test_does_not_exist\0").err().unwrap(); 106 } 107} 108 109#[test] 110fn interior_null_fails() { 111 make_helpers(); 112 unsafe { 113 let lib = Library::new(lib_path()).unwrap(); 114 lib.get::<*mut ()>(b"test_does\0_not_exist").err().unwrap(); 115 lib.get::<*mut ()>(b"test\0_does_not_exist\0") 116 .err() 117 .unwrap(); 118 } 119} 120 121#[test] 122fn test_incompatible_type() { 123 make_helpers(); 124 unsafe { 125 let lib = Library::new(lib_path()).unwrap(); 126 assert!(match lib.get::<()>(b"test_identity_u32\0") { 127 Err(libloading::Error::IncompatibleSize) => true, 128 _ => false, 129 }) 130 } 131} 132 133#[test] 134fn test_incompatible_type_named_fn() { 135 make_helpers(); 136 unsafe fn get<'a, T>(l: &'a Library, _: T) -> Result<Symbol<'a, T>, libloading::Error> { 137 l.get::<T>(b"test_identity_u32\0") 138 } 139 unsafe { 140 let lib = Library::new(lib_path()).unwrap(); 141 assert!(match get(&lib, test_incompatible_type_named_fn) { 142 Err(libloading::Error::IncompatibleSize) => true, 143 _ => false, 144 }) 145 } 146} 147 148#[test] 149fn test_static_u32() { 150 make_helpers(); 151 unsafe { 152 let lib = Library::new(lib_path()).unwrap(); 153 let var: Symbol<*mut u32> = lib.get(b"TEST_STATIC_U32\0").unwrap(); 154 **var = 42; 155 let help: Symbol<unsafe extern "C" fn() -> u32> = 156 lib.get(b"test_get_static_u32\0").unwrap(); 157 assert_eq!(42, help()); 158 } 159} 160 161#[test] 162fn test_static_ptr() { 163 make_helpers(); 164 unsafe { 165 let lib = Library::new(lib_path()).unwrap(); 166 let var: Symbol<*mut *mut ()> = lib.get(b"TEST_STATIC_PTR\0").unwrap(); 167 **var = *var as *mut _; 168 let works: Symbol<unsafe extern "C" fn() -> bool> = 169 lib.get(b"test_check_static_ptr\0").unwrap(); 170 assert!(works()); 171 } 172} 173 174#[test] 175// Something about i686-pc-windows-gnu, makes dll initialisation code call abort when it is loaded 176// and unloaded many times. So far it seems like an issue with mingw, not libloading, so ignoring 177// the target. Especially since it is very unlikely to be fixed given the state of support its 178// support. 179#[cfg(not(all(target_arch = "x86", target_os = "windows", target_env = "gnu")))] 180fn manual_close_many_times() { 181 make_helpers(); 182 let join_handles: Vec<_> = (0..16) 183 .map(|_| { 184 std::thread::spawn(|| unsafe { 185 for _ in 0..10000 { 186 let lib = Library::new(lib_path()).expect("open library"); 187 let _: Symbol<unsafe extern "C" fn(u32) -> u32> = 188 lib.get(b"test_identity_u32").expect("get fn"); 189 lib.close().expect("close is successful"); 190 } 191 }) 192 }) 193 .collect(); 194 for handle in join_handles { 195 handle.join().expect("thread should succeed"); 196 } 197} 198 199#[cfg(unix)] 200#[test] 201fn library_this_get() { 202 use libloading::os::unix::Library; 203 make_helpers(); 204 // SAFE: functions are never called 205 unsafe { 206 let _lib = Library::new(lib_path()).unwrap(); 207 let this = Library::this(); 208 // Library we loaded in `_lib` (should be RTLD_LOCAL). 209 assert!(this 210 .get::<unsafe extern "C" fn()>(b"test_identity_u32") 211 .is_err()); 212 // Something obscure from libc... 213 assert!(this.get::<unsafe extern "C" fn()>(b"freopen").is_ok()); 214 } 215} 216 217#[cfg(windows)] 218#[test] 219fn library_this() { 220 use libloading::os::windows::Library; 221 make_helpers(); 222 unsafe { 223 // SAFE: well-known library without initialisers is loaded. 224 let _lib = Library::new(lib_path()).unwrap(); 225 let this = Library::this().expect("this library"); 226 // SAFE: functions are never called. 227 // Library we loaded in `_lib`. 228 assert!(this 229 .get::<unsafe extern "C" fn()>(b"test_identity_u32") 230 .is_err()); 231 // Something "obscure" from kernel32... 232 assert!(this.get::<unsafe extern "C" fn()>(b"GetLastError").is_err()); 233 } 234} 235 236#[cfg(windows)] 237#[test] 238fn works_getlasterror() { 239 use libloading::os::windows::{Library, Symbol}; 240 use winapi::shared::minwindef::DWORD; 241 use winapi::um::errhandlingapi; 242 243 unsafe { 244 let lib = Library::new("kernel32.dll").unwrap(); 245 let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError").unwrap(); 246 errhandlingapi::SetLastError(42); 247 assert_eq!(errhandlingapi::GetLastError(), gle()) 248 } 249} 250 251#[cfg(windows)] 252#[test] 253fn works_getlasterror0() { 254 use libloading::os::windows::{Library, Symbol}; 255 use winapi::shared::minwindef::DWORD; 256 use winapi::um::errhandlingapi; 257 258 unsafe { 259 let lib = Library::new("kernel32.dll").unwrap(); 260 let gle: Symbol<unsafe extern "system" fn() -> DWORD> = lib.get(b"GetLastError\0").unwrap(); 261 errhandlingapi::SetLastError(42); 262 assert_eq!(errhandlingapi::GetLastError(), gle()) 263 } 264} 265 266#[cfg(windows)] 267#[test] 268fn library_open_already_loaded() { 269 use libloading::os::windows::Library; 270 271 // Present on Windows systems and NOT used by any other tests to prevent races. 272 const LIBPATH: &str = "Msftedit.dll"; 273 274 // Not loaded yet. 275 assert!(match Library::open_already_loaded(LIBPATH) { 276 Err(libloading::Error::GetModuleHandleExW { .. }) => true, 277 _ => false, 278 }); 279 280 unsafe { 281 let _lib = Library::new(LIBPATH).unwrap(); 282 // Loaded now. 283 assert!(Library::open_already_loaded(LIBPATH).is_ok()); 284 } 285} 286