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