blob: 24409c16e8fea76823a4a9e2f76d9ef07ff923af [file]
/// Verify that we can load a cdylib, call functions from it, and then unload it.
#[cfg(windows)]
mod libloading {
type BOOL = i32;
type DWORD = u32;
type HANDLE = isize;
pub type HMODULE = isize;
type FARPROC = *mut core::ffi::c_void;
#[link(name = "kernel32")]
unsafe extern "system" {
fn LoadLibraryExW(filename: *const u16, file: HANDLE, flags: DWORD) -> HMODULE;
fn FreeLibrary(module: HMODULE) -> BOOL;
fn GetProcAddress(module: HMODULE, procname: *const u8) -> FARPROC;
}
fn wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(core::iter::once(0)).collect()
}
fn ansi_null(s: &str) -> Vec<u8> {
assert!(!s.as_bytes().contains(&0), "symbol name must not contain interior NUL");
s.as_bytes().iter().copied().chain(core::iter::once(0)).collect()
}
pub fn load(lib_name: &str) -> Option<HMODULE> {
let filename = wide_null(&format!("{}.dll", lib_name));
let handle = unsafe { LoadLibraryExW(filename.as_ptr(), 0, 0) };
if handle == 0 { None } else { Some(handle) }
}
pub fn get_symbol(handle: HMODULE, name: &str) -> Option<FARPROC> {
let symbol_name = ansi_null(name);
let symbol = unsafe { GetProcAddress(handle, symbol_name.as_ptr()) };
if symbol.is_null() { None } else { Some(symbol) }
}
pub fn unload(handle: HMODULE) {
unsafe {
FreeLibrary(handle);
}
}
}
#[cfg(unix)]
mod libloading {
use std::ffi::{c_char, c_int, c_void};
const RTLD_NOW: c_int = 2;
fn cstr_null(s: &str) -> Vec<c_char> {
assert!(!s.as_bytes().contains(&0), "string must not contain interior NUL");
s.as_bytes().iter().copied().chain(core::iter::once(0)).map(|b| b as c_char).collect()
}
pub fn load(lib_name: &str) -> Option<*mut c_void> {
#[cfg(target_os = "macos")]
let filename = cstr_null(&format!("lib{}.dylib", lib_name));
#[cfg(not(target_os = "macos"))]
let filename = cstr_null(&format!("./lib{}.so", lib_name));
let handle = unsafe { dlopen(filename.as_ptr(), RTLD_NOW) };
if handle.is_null() { None } else { Some(handle) }
}
pub fn get_symbol(handle: *mut c_void, name: &str) -> Option<*mut c_void> {
let symbol_name = cstr_null(name);
let symbol = unsafe { dlsym(handle, symbol_name.as_ptr()) };
if symbol.is_null() { None } else { Some(symbol) }
}
pub fn unload(handle: *mut c_void) {
unsafe {
dlclose(handle);
}
}
#[cfg_attr(any(target_os = "linux", target_os = "android"), link(name = "dl"))]
extern "C" {
fn dlopen(filename: *const c_char, flags: core::ffi::c_int) -> *mut c_void;
fn dlclose(handle: *mut c_void) -> core::ffi::c_int;
fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
}
}
type ExternFn = unsafe extern "C" fn(u32, u32) -> u32;
fn main() {
let foo = libloading::load("foo").expect("Failed to load library");
println!("loaded library");
let extern_fn_1 = libloading::get_symbol(foo, "extern_fn_1").expect("Failed to find symbol");
let extern_fn_1: ExternFn = unsafe { std::mem::transmute(extern_fn_1) };
let result = unsafe { extern_fn_1(2, 3) };
println!("result of extern_fn_1(2, 3): {}", result);
let extern_fn_2 = libloading::get_symbol(foo, "extern_fn_2").expect("Failed to find symbol");
let extern_fn_2: ExternFn = unsafe { std::mem::transmute(extern_fn_2) };
let result = unsafe { extern_fn_2(2, 3) };
println!("result of extern_fn_2(2, 3): {}", result);
libloading::unload(foo);
println!("unloaded library");
}