Rust:从动态加载的库执行特定代码行时的Segfault

时间:2014-10-28 06:45:56

标签: segmentation-fault rust dlopen dynamic-library

我在Rust中编写了一个简单的基于插件的系统,以获得使用该语言的一些技能和经验。我的系统动态加载库并在运行时执行它们以初始化每个插件。在从动态加载的库中执行代码时,我遇到了一个有趣的段错误问题。

这是加载和运行插件初始化函数的代码:(这个位工作正常)

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);
}

这是插件库中的init函数:

#[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.");
}

如上所述,segfault发生在名为ps的PluginSystem结构的register_plugin函数中。这个结构来自调用方法(在第一个代码块中)。

这是PluginSystem中的register_plugin函数:

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);
    };
}

在此代码块中执行HashMap::new()时会发生段错误;

我尝试过以不同方式实现此功能,如:

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) => {  }
            }
}

但我得到完全相同的问题。

如果我跳过register_plugin函数,并在动态加载的库中运行其他代码,它可以正常工作。实际上,这个段错误的唯一代码是HashMap::new()

这是一个错误还是现有问题,还是我做错了什么?

更多信息: 我用调试符号编译了rust,以便逐步查看HashMap代码来查找问题。在调试代码时,看起来它甚至不会尝试执行new()函数,当调试器调入HashMap::new()时,调试器会直接在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())
    };

Segfault发生在cause.take()函数中,我认为这是因为my_ep.cause为null或无法访问。因此,某些事情会产生无效的异常,并且try函数会阻塞它并给出段错误。这与从动态加载的库中调用HashMap代码有关,但我不知道它是如何相关的。

感谢您的帮助。

编辑: 我的平台是linux x64,我使用了git master从昨天(10月28日和14日)开始的新生锈。

1 个答案:

答案 0 :(得分:2)

我在GitHub上打开了一个关于这个(here)的问题,只有一个最小的测试用例,它在几个小时内就解决了。

基本上,这是因为在构建可执行文件时,rustc默认静态链接libstd,在构建库时动态链接。 所以主要是使用静态libstd,并且在运行时加载plugin.so时,插件使用动态libstd。之后发生了奇怪的记忆问题。

通过将-C prefer-dynamic添加到主可执行文件的rustc来修复。