#![allow(clippy::multiple_unsafe_ops_per_block)]
use std::ffi::{CStr, c_char, c_int};
use dlsym::{Error, Library, library_filename};
#[cfg(unix)]
fn libc() -> Library {
Library::this().unwrap()
}
#[cfg(windows)]
fn libc() -> Library {
unsafe { Library::open("msvcrt.dll") }.unwrap()
}
#[test]
fn variadic_call() {
let lib = libc();
let sprintf = unsafe {
lib.get::<unsafe extern "C" fn(*mut c_char, *const c_char, ...) -> c_int>(c"sprintf")
}
.unwrap();
let mut buf = [0 as c_char; 32];
let n = unsafe { sprintf.with(|f| f(buf.as_mut_ptr(), c"%d-%s".as_ptr(), 42, c"ok".as_ptr())) };
assert_eq!(n, 5);
assert_eq!(unsafe { CStr::from_ptr(buf.as_ptr()) }, c"42-ok");
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
#[test]
fn data_symbol() {
let lib = libc();
let name = unsafe { lib.get::<*mut *const c_char>(c"program_invocation_name") }.unwrap();
let name = unsafe { CStr::from_ptr(*name.as_ref()) };
assert!(!name.to_bytes().is_empty());
}
#[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))]
#[test]
fn versioned_symbol() {
use dlsym::os::unix::LibraryExt;
let lib = libc();
let memcpy = unsafe { lib.get_versioned::<*const u8>(c"memcpy", c"GLIBC_2.14") }.unwrap();
assert!(!memcpy.as_ptr().is_null());
let err = unsafe { lib.get_versioned::<*const u8>(c"memcpy", c"DLSYM_9.9") }.unwrap_err();
assert!(matches!(err, Error::SymbolNotFound { .. }), "{err:?}");
}
#[cfg(target_os = "linux")]
#[test]
fn open_options() {
use dlsym::os::unix::OpenOptions;
let libm = unsafe { OpenOptions::new().lazy(true).global(true).open("libm.so.6") }.unwrap();
let again = unsafe { OpenOptions::new().no_load(true).open("libm.so.6") }.unwrap();
drop(again);
drop(libm);
let missing = unsafe { OpenOptions::new().no_load(true).open("libdlsym-missing.so") };
assert!(missing.is_err());
}
#[cfg(windows)]
#[test]
fn already_loaded() {
use dlsym::os::windows::LibraryExt;
let kernel32 = Library::open_already_loaded("kernel32.dll").unwrap();
let f = unsafe { kernel32.get::<*const u8>(c"GetLastError") }.unwrap();
assert!(!f.as_ptr().is_null());
assert!(Library::open_already_loaded("dlsym-definitely-missing.dll").is_err());
}
#[test]
fn open_first_returns_first_loadable() {
let candidates = [
"libdlsym-missing-1",
#[cfg(target_os = "linux")]
"libm.so.6",
#[cfg(target_vendor = "apple")]
"/usr/lib/libm.dylib",
#[cfg(windows)]
"ucrtbase.dll",
];
unsafe { Library::open_first(candidates) }.unwrap();
let err =
unsafe { Library::open_first(["libdlsym-missing-1", "libdlsym-missing-2"]) }.unwrap_err();
match err {
Error::Open { path, .. } => assert_eq!(path, "libdlsym-missing-2"),
e => panic!("{e:?}"),
}
assert!(unsafe { Library::open_first(Vec::<&str>::new()) }.is_err());
}
#[test]
fn leaked_symbols_are_static() {
let lib: &'static Library = libc().leak();
let strlen: dlsym::Symbol<'static, unsafe extern "C" fn(*const c_char) -> usize> =
unsafe { lib.get(c"strlen") }.unwrap();
assert_eq!(unsafe { strlen.call((c"four".as_ptr(),)) }, 4);
}
#[test]
fn filenames() {
let name = library_filename("z");
if cfg!(windows) {
assert_eq!(name, "z.dll");
} else if cfg!(target_vendor = "apple") {
assert_eq!(name, "libz.dylib");
} else {
assert_eq!(name, "libz.so");
}
}