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