1#[cfg(windows)]
2extern crate winapi;
3
4extern crate libloading;
5use libloading::{Library, Symbol};
6
7const TARGET_DIR: Option<&'static str> = option_env!("CARGO_TARGET_DIR");
8const TARGET_TMPDIR: Option<&'static str> = option_env!("CARGO_TARGET_TMPDIR");
9
10fn 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
19fn 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]
38fn 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)]
49struct S {
50    a: u64,
51    b: u32,
52    c: u16,
53    d: u8,
54}
55
56#[test]
57fn 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]
80fn 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]
91fn wrong_name_fails() {
92    unsafe {
93        Library::new("target/this_location_is_definitely_non existent:^~")
94            .err()
95            .unwrap();
96    }
97}
98
99#[test]
100fn 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]
110fn 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]
122fn 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]
134fn 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]
149fn 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]
162fn 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")))]
180fn 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]
201fn 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]
219fn 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]
238fn 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]
253fn 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]
268fn 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