来自 Rust cdylib 的恐慌导致致命的运行时错误

Panic from Rust cdylib is causing a fatal runtime error

提问人:Johnny Bakker 提问时间:11/15/2023 更新时间:11/15/2023 访问量:41

问:

我目前正在开发一个插件加载系统,该系统利用 C++ Boost 来加载 Rust cdylib 模块。一切都正常运行,除了我在 cdylib 中调用时。崩溃源自一个单独的线程,导致整个应用程序终止。我正在积极寻找解决方案,以防止主应用程序受到影响。有没有人对如何实现这一目标有建议?panic!()

插件 lib.rs

struct FirstPlugin;

impl Plugin for FirstPlugin {
    fn load(&mut self) {
    println!("Load");
    panic!("Test");
    }

    fn unload(&mut self) {
        println!("Unload")
    }
}

impl Default for FirstPlugin {
    fn default() -> Self {
        Self {  }
    }
}

#[no_mangle]
#[link_section = ".vtable"]
pub unsafe extern "C" fn __FIRST_PLUGIN_VTABLE() -> ffi::PluginVTable {
    ffi::PluginVTable::new::<FirstPlugin>()
}

FFI Vtable(FFI 虚拟表格)

#[repr(C)]
#[derive(Clone, Copy)]
pub struct PluginVTable {
    
    // Construct and destruct
    pub construct: extern fn() -> *mut(),
    pub destruct: extern fn(*mut()),

    // Member methods
    pub load: extern fn(*mut ()),
    pub unload: extern fn(*mut ())

}

impl PluginVTable {
    
    pub unsafe fn new<T: Plugin>() -> PluginVTable {
    PluginVTable { 
            construct: std::mem::transmute(PluginVTable::construct::<T> as *mut()), 
        destruct: std::mem::transmute(PluginVTable::destruct::<T> as *mut()), 
        load: std::mem::transmute(T::load as *mut()), 
            unload: std::mem::transmute(T::unload as *mut()) 
    }
    }

    fn construct<T: Plugin>() -> *mut T {
    let plugin = Box::new(T::default());
    Box::into_raw(plugin)
    }

    unsafe fn destruct<T: Plugin>(plugin: *mut T) {
    let _ = Box::from_raw(plugin);
    }
}

创建线程和调用方法

pub struct PluginInstance {
    instance: Option<std::thread::JoinHandle<PluginData>>
}

impl PluginInstance {

    fn new(vtable: PluginVTable) -> Self {

        let instance = std::thread::spawn(move||{
            PluginInstance::instance_thread(vtable)
        });

    Self { instance: Some(instance) }
    }

    fn instance_thread(vtable: PluginVTable) -> PluginData {
        
    let ptr = (vtable.construct)();
    (vtable.load)(ptr);

    //std::thread::park();
    //(vtable.unload)(ptr);
        //(vtable.destruct)(ptr);

    PluginData
    }
}

impl From<PluginVTable> for PluginInstance {
    fn from(vtable: PluginVTable) -> Self {
    PluginInstance::new(vtable)
    }
}

impl Drop for PluginInstance {
    fn drop(&mut self) {
    println!("Dropping instance")
        //if let Some(instance) = self.instance.take() {
    // instance.thread().unpark();
    // match instance.join() {
    //  Ok(data) => println!("Plugin ended succesfully {:?}", data),
    //  Err(e) => println!("Plugin ended with an error {:?}", e),
    //}
    //}
    }
}

带回溯的输出

    Finished dev [unoptimized + debuginfo] target(s) in 0.09s
     Running `target\debug\plug.exe`
Load
thread '<unnamed>' panicked at 'Test', src\lib.rs:8:9
stack backtrace:
   0:     0x7ffe7135871c - std::sys_common::backtrace::_print::impl$0::fmt
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\sys_common\backtrace.rs:44
   1:     0x7ffe7136712b - core::fmt::rt::Argument::fmt
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\core\src\fmt\rt.rs:138
   2:     0x7ffe7136712b - core::fmt::write
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\core\src\fmt\mod.rs:1094
   3:     0x7ffe713569ef - std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\io\mod.rs:1714
   4:     0x7ffe713584cb - std::sys_common::backtrace::_print
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\sys_common\backtrace.rs:47
   5:     0x7ffe713584cb - std::sys_common::backtrace::print
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\sys_common\backtrace.rs:34
   6:     0x7ffe7135a19a - std::panicking::default_hook::closure$1
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\panicking.rs:269
   7:     0x7ffe71359def - std::panicking::default_hook
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\panicking.rs:288
   8:     0x7ffe7135a84e - std::panicking::rust_panic_with_hook
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\panicking.rs:705
   9:     0x7ffe7135a6fa - std::panicking::begin_panic_handler::closure$0
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\panicking.rs:595
  10:     0x7ffe71359099 - std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\sys_common\backtrace.rs:151
  11:     0x7ffe7135a440 - std::panicking::begin_panic_handler
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\panicking.rs:593
  12:     0x7ffe7136c1d5 - core::panicking::panic_fmt
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\core\src\panicking.rs:67
  13:     0x7ffe71351886 - first::impl$0::load
                               at C:\Users\Johnny\Desktop\plug-rs\plugins\first\src\lib.rs:8
  14:     0x7ff79719425b - plug_loader::PluginInstance::instance_thread
                               at C:\Users\Johnny\Desktop\plug-rs\loader\src\lib.rs:29
  15:     0x7ff797198001 - plug_loader::impl$0::new::closure$0
                               at C:\Users\Johnny\Desktop\plug-rs\loader\src\lib.rs:19
  16:     0x7ff797199339 - std::sys_common::backtrace::__rust_begin_short_backtrace<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData>
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\std\src\sys_common\backtrace.rs:135
  17:     0x7ff797199339 - std::sys_common::backtrace::__rust_begin_short_backtrace<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData>
  19:     0x7ff797199371 - core::panic::unwind_safe::impl$23::call_once<plug_loader::PluginData,std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData> >
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\core\src\panic\unwind_safe.rs:271
  20:     0x7ff79719651a - std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData> >,plug_loader::PluginData>
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\std\src\panicking.rs:500
  21:     0x7ff7971966d3 - std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$7::drop::closure_env$0<plug_loader::PluginData> >,tuple$<> >
  22:     0x7ff7971963f4 - std::panicking::try<plug_loader::PluginData,core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData> > >
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\std\src\panicking.rs:464
  23:     0x7ff797195289 - std::panic::catch_unwind
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\std\src\panic.rs:142
  24:     0x7ff797195289 - std::thread::impl$0::spawn_unchecked_::closure$1<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData>
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\std\src\thread\mod.rs:528
  25:     0x7ff79719228e - core::ops::function::FnOnce::call_once<std::thread::impl$0::spawn_unchecked_::closure_env$1<plug_loader::impl$0::new::closure_env$0,plug_loader::PluginData>,tuple$<> >
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be\library\core\src\ops\function.rs:250
  26:     0x7ff7971a837c - std::sys::windows::thread::impl$0::new::thread_start
                               at /rustc/5680fa18feaa87f3ff04063800aec256c3d4b4be/library\std\src\sys\windows\thread.rs:57
  27:     0x7ffeb600257d - BaseThreadInitThunk
  28:     0x7ffeb758aa58 - RtlUserThreadStart
fatal runtime error: Rust cannot catch foreign exceptions
error: process didn't exit successfully: `target\debug\plug.exe` (exit code: 0xc0000409, STATUS_STACK_BUFFER_OVERRUN)

我尝试同时使用和.还尝试用 .panic=abortpanic=unwind(vtable.load)(ptr)std::panic::catch_unwind

C++ Rust Boost DLL 崩溃

评论

1赞 cafce25 11/15/2023
您不应该一开始就发出错误信号。在大多数情况下,跨越 ffi 边界的恐慌是 UB。panic
0赞 Johnny Bakker 11/15/2023
感谢您的评论!我知道恐慌不是发出错误信号的方式,但可悲的是,有时它会发生。当它发生时,希望能够以干净的方式关闭其他插件,甚至更好地让它们保持活力;)。

答:

0赞 Johnny Bakker 11/15/2023 #1

通过在将函数指针添加到 vtable 之前将其包装起来来解决。catch_unwind

pub unsafe fn new<T: Plugin>() -> PluginVTable {
    PluginVTable { 
        load: std::mem::transmute(PluginVTable::load::<T> as *mut())
    }
}

unsafe fn load<T: Plugin>(ptr: *mut T) -> *mut() {
    match catch_unwind(||{ T::load(&mut *ptr) }) {
        Ok(_) => std::ptr::null_mut(),
        Err(e) => Box::into_raw(e) as *mut(),
    }
}

然后,呼叫者可以使用 恢复 panic。resume_unwind

let result = (vtable.load)(ptr) as *mut (dyn Any + Send);

if !result.is_null() {
    std::panic::resume_unwind(Box::from_raw(result))
}