0

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).

The API is: https://github.com/libguestfs/nbdkit/blob/409ce4c9238a84ede6688423b20d5f706067834b/include/nbdkit-plugin.h#L53

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.

3
  • 1
    Your structure en Rust doesn't match the structure in C and this is totally UB also please produce an minimal reproducible example. And consider use bindgen. Commented Dec 1, 2018 at 21:30
  • It matches enough of the structure to make the point. By dumping out the fields from the C program I can see that the integers and strings are fine, but the function pointers are bogus. Commented Dec 2, 2018 at 18:29
  • There is no such thing that "matches enough" or "but the function pointers are bogus", undefined behavior is undefined behavior. You can't assume this doesn't cause the bug. Do thing properly and then look if this is still bogus. Also I repeat myself please do an minimal reproducible example. For exemple try to make a new C lib with a structure with only one function and try it without the all mess of this API. Commented Dec 2, 2018 at 19:05

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

If I don't use the Option for longname etc, then how can I specify them as being NULL?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.