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