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