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