x4e
November 12, 2020, 8:12pm
1
Is there a way to declare an optional external function that can be linked at runtime?
If for example I am running code in a process that may or may not have a shared object attached that may or may not export a function, is there a way of accessing the exported function without panicking if it cannot be found (which is the default behavior of extern "C" functions)?
While I could use operating system apis such as dlsym I'd prefer not to as these functions are being declared as part of a library and I do not want to require such a setup process.
This is the original code, that will panic at runtime if the function exit
cannot be found:
fn main() {
unsafe {
exit(0);
panic!();
}
}
extern "C" {
pub fn exit(code: ::std::os::raw::c_int);
}
One idea I had was to declare it is a global variable like so:
fn main() {
unsafe {
exit.unwrap()(0);
panic!();
}
}
extern "C" {
pub static exit: Option<extern "C" fn(code: ::std::os::raw::c_int)>;
}
This however results in a segmentation fault.
Am I implementing this wrong? Or are there any other ways I can achieve this.
Thanks
matklad
November 12, 2020, 8:23pm
2
The technical term you need is "weak linkage". See
1 Like
x4e
November 12, 2020, 9:54pm
3
Thank you so much for the guidance! Using the weak utility you provided I created the following class that should work on windows and posix:
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{marker, mem};
use std::ffi::CString;
use std::os::raw::c_char;
pub struct Weak<F> {
name: &'static str,
addr: AtomicUsize,
_marker: marker::PhantomData<F>,
}
impl<F> Weak<F> {
pub const fn new(name: &'static str) -> Weak<F> {
Weak {
name,
addr: AtomicUsize::new(1),
_marker: marker::PhantomData,
}
}
pub fn get(&self) -> Option<&F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 1 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
if self.addr.load(Ordering::SeqCst) == 0 {
None
} else {
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}
}
unsafe fn fetch(name: &str) -> usize {
let name = match CString::new(name) {
Ok(cstr) => cstr,
Err(..) => return 0,
};
find_symbol(name.as_ptr())
}
#[cfg(unix)]
unsafe fn find_symbol(name: *const c_char) -> usize {
use libc::{dlsym, RTLD_DEFAULT};
dlsym(RTLD_DEFAULT, name) as usize
}
#[cfg(windows)]
unsafe fn find_symbol(name: *const c_char) -> usize {
use std::ptr::null;
use kernel32::{GetModuleHandle, GetProcAddress};
GetProcAddress(GetModuleHandleA(null()), name) as usize
}
1 Like
matklad
November 12, 2020, 10:07pm
4
You probably want #[cfg(unix)]
there
1 Like
system
Closed
February 10, 2021, 10:07pm
5
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.