Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust: Segfault when executing specific line of code from a dynamically loaded library

Im writing a simple plugin-based system in Rust to gain some skills and experience using the language. My system dynamically loads libraries and executes them at runtime to initialize each plugin. Im running into an interesting segfault issue when executing code from a dynamically-loaded library.

This is the code to load and run the plugin init function: (this bit works fine)

pub fn register_plugins<'rp>(&'rp mut self)
{
    let p1 = match DynamicLibrary::open(Some("librust_plugin_1.so")) {
            Ok(lib) => lib,
            Err(error) => fail!("Could not load the library: {}", error)
    };
    let s1: extern "Rust" fn(&PluginSystem) = unsafe {
        match p1.symbol("init") {
            Err(error) => fail!("Could not load function init: {}", error),
            Ok(init) => mem::transmute::<*mut u8, _>(init)
        }
    };
    s1(&self.ps);
}

This is the init function within the plugin library:

#[no_mangle]
pub fn init(ps:&mut PluginSystem)
{
    ps.register_plugin("ps1"); //<-- Segfault is in this method
    ps.add_new_hook_with_closure("onLoad", "ps1", "display greeting.", 10, test1);
    println!("Initialized plugin.");
}

As commented, the segfault occurs within the register_plugin function of the PluginSystem struct named ps. This struct is borrowed from the calling method (in the first code chunk).

This is the register_plugin function in PluginSystem:

pub fn register_plugin(&mut self, plugin_name: &'s str)
{
    if ! self.plugins.contains_key(&plugin_name) {
        let hm = HashMap::new(); //<-- Segfault Here
        self.plugins.insert(plugin_name, hm);
    };
}

The segfault occurs when executing HashMap::new() in this code block;

I have tried implementing this function differently, as in:

pub fn register_plugin(&mut self, plugin_name: &'s str)
{
    match self.plugins.entry(plugin_name) {
                Vacant(entry) => {
                        entry.set(HashMap::new()); //<-- Segfault Here
                    }
                Occupied(mut entry) => {  }
            }
}

But I get exactly the same issue.

If I skip the register_plugin function, and run other code in the dynamically-loaded library, it works fine. In fact the only code which this segfaults on is HashMap::new().

Is this a bug or an existing issue, or am I doing something wrong?

More Info: I compiled rust with debugging symbols in order to step through HashMap code to find the issue. It looks like it doesn't even try to execute the new() function, when debugging the code, when stepping into HashMap::new() the debugger steps directly to this function in unwind.rs:

pub unsafe fn try(f: ||) -> ::core::result::Result<(), Box<Any + Send>> {
    let closure: Closure = mem::transmute(f);
    let ep = rust_try(try_fn, closure.code as *mut c_void,
                  closure.env as *mut c_void);
    return if ep.is_null() {
        Ok(())
    } else {
        let my_ep = ep as *mut Exception; //<-- Steps into this line
        rtdebug!("caught {}", (*my_ep).uwe.exception_class);
        let cause = (*my_ep).cause.take(); //<-- Segfaults Here
        uw::_Unwind_DeleteException(ep);
        Err(cause.unwrap())
    };

The Segfault occurs in the cause.take() function, I think it is because my_ep.cause is null or inaccessible. So something is generating an invalid exception, and try function is choking on it and giving the segfault. This is related to calling the HashMap code from the dynamically-loaded library, but I don't know how it is related.

Thanks for any help.

EDIT: My platform is linux x64, im using a freshly built rust from git master as of yesterday (28 Oct '14).

like image 547
Ashley Sommer Avatar asked Oct 31 '22 15:10

Ashley Sommer


1 Answers

I opened an issue on GitHub about this (here), with a minimal test case, and it was solved in a few hours.

Basically, it was because rustc default to statically linking libstd when building executables, and dynamically linking when building libraries. So main was using a static libstd, and when loading plugin.so at runtime, the plugin uses a dynamic libstd. Weird memory issues happen after that.

Fixed by adding -C prefer-dynamic to rustc for the main executable.

like image 84
Ashley Sommer Avatar answered Jan 04 '23 14:01

Ashley Sommer