nagisa / rust_libloading

Bindings around the platform's dynamic library loading primitives with greatly improved memory safety.
https://docs.rs/libloading
ISC License
1.22k stars 100 forks source link

SEGFAULT on macOS when use `dyn Trait` on Symbols #151

Open RandomInsano opened 3 months ago

RandomInsano commented 3 months ago

I'm nearly certain I'm doing something architecturally wrong so I'll start from the problem I'm trying to solve and someone can tell me the right way to do this. 😁

I'd like to create a pluggable program that lets you drop DLLs into a folder to handle certain events occurring (item creation, updates, deletion). To do that, I started using libloading and all was well until I wanted to start using a trait so that plugin authors could store additional data in their structs. I have a weird interface to create an instance of a plugin given Rust's trait object safety rules and it looks like this:

pub trait ObjectPlugin {
    /// Initialize things for the plugin
    extern "C" fn init() -> Box<dyn ObjectPlugin> where Self: Sized;
    /// Unload plugin
    extern "C" fn unload(self: &Self);

    extern "C" fn on_object_updated<'a>(self: &mut Self, object: &mut Object, post: &Post) -> bool;
}

This API is just for experimentation for now so I'm very open to tearing it all down (and also returning Result instead of a bool) but what happens when I use dyn ObjectPlugin instead of the struct supporting it, is fun!

I'm using VSCode and the lldb debugger and it looks like the data being pushed onto the stack is out of alignment. I'd hoped that a mutable borrow would default to a pointer but it seems like it's possibly being optimized out? For example when calling on_object_updated the Object struct is a mess but part of it shows up in the Post struct right after it. That makes me think that the "host" side is leaving room for self and the "plugin" side is optimizing it to a smaller size.

Some other fun facts:

I'm a bit out of my element here, and I'm happy to provide the whole project up on Github if it's helpful. If there's already a project doing something like this, I can also just copy their homework. :)

Thanks!

RandomInsano commented 3 months ago

Mmm. Had a realization after cutting this issue that dyn might not be FFI-safe and it seems no? There's a crate here: https://github.com/outfoxxed/dyntable

Still, I'm curious about how someone might implement this and I can write some documentation and present it as a PR.

nagisa commented 2 months ago

You may want to look into e.g. glib/gobject for ABI safe objects and such. But more generally there isn't much specific to libloading here. I can only direct you at the users.rust-lang.org forum or some other location where you'll have a larger pool of people with some great ideas.