• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #[cfg(windows)]
2 extern crate winapi;
3 
4 extern crate libloading;
5 use libloading::{Library, Symbol};
6 
7 const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR");
8 const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR");
9 
lib_path() -> std::path::PathBuf10 fn 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 
make_helpers()19 fn 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]
test_id_u32()38 fn 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)]
49 struct S {
50     a: u64,
51     b: u32,
52     c: u16,
53     d: u8,
54 }
55 
56 #[test]
test_id_struct()57 fn 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]
test_0_no_0()80 fn 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]
wrong_name_fails()91 fn wrong_name_fails() {
92     unsafe {
93         Library::new("target/this_location_is_definitely_non existent:^~")
94             .err()
95             .unwrap();
96     }
97 }
98 
99 #[test]
missing_symbol_fails()100 fn 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]
interior_null_fails()110 fn 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]
test_incompatible_type()122 fn 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]
test_incompatible_type_named_fn()134 fn 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]
test_static_u32()149 fn 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]
test_static_ptr()162 fn 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")))]
manual_close_many_times()180 fn 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]
library_this_get()201 fn 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]
library_this()219 fn 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]
works_getlasterror()238 fn 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]
works_getlasterror0()253 fn 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]
library_open_already_loaded()268 fn 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