Reputation: 976
I'm trying to make a Rust binding to nbdkit without much luck. I need to make a .so
file, which is easy. The .so
file must have a public function called plugin_init
, also easy. However this function must return a pointer to a C-compatible struct
containing a mix of strings and function pointers (that the C main program will later call).
I came up with:
#[repr(C)]
pub struct NBDKitPlugin {
_struct_size: uint64_t,
_api_version: c_int,
_thread_model: c_int,
name: *const c_char,
longname: Option<*const c_char>,
version: Option<*const c_char>,
description: Option<*const c_char>,
load: Option<extern fn ()>,
unload: Option<extern fn ()>,
config: Option<extern fn ()>, // XXX
config_complete: Option<extern fn () -> c_int>,
config_help: Option<*const c_char>,
open: extern fn (c_int) -> *mut c_void,
close: Option<extern fn (*mut c_void)>,
}
and a plugin_init
function:
extern fn hello_load () {
println! ("hello this is the load method");
}
struct MyHandle {
}
extern fn hello_open (readonly: c_int) -> *mut c_void {
println! ("hello, this is the open method");
let mut h = MyHandle {};
let vp: *mut c_void = &mut h as *mut _ as *mut c_void;
return vp;
}
#[no_mangle]
pub extern fn plugin_init () -> *const NBDKitPlugin {
println! ("hello from the plugin");
let plugin = Box::new (NBDKitPlugin {
_struct_size: mem::size_of::<NBDKitPlugin>() as uint64_t,
_api_version: 2,
_thread_model: 3,
name: CString::new("hello").unwrap().into_raw(),
longname: None,
version: None,
description: None,
load: Some (hello_load),
unload: None,
config: None,
config_complete: None,
config_help: Some (CString::new("my config_help here").unwrap().into_raw()),
open: hello_open,
close: None,
});
return Box::into_raw(plugin);
}
Apart from leaking memory, this partially works. The integers and strings are seen from C OK. However the function pointers don't work at all. They are completely bogus and seem to occupy more space than a raw pointer so I suppose that I'm exposing a "fat" Rust pointer.
There seems very little documentation on this topic that I can find. Help.
Upvotes: 0
Views: 530
Reputation: 65935
You've probably learned at some point that a reference (&T
) wrapped in an Option
(Option<&T>
) is optimized such that None
is encoded as all zeroes, which is not valid for a reference, and Option<&T>
has the same size as &T
.
However, all zeroes is a valid bit pattern for a raw pointer (*const T
or *mut T
): it represents the null pointer. As such, wrapping those in an Option
is no different from wrapping, say, an i32
in an Option
: the Option
type is larger so that it can store a discriminant.
To fix your struct definition, you must not use Option
to define longname
, version
and description
.
Upvotes: 1