调用cdylib crate返回的函数时Rust出现段错误的问题排查
解决Rust动态库中传递函数指针导致的段错误问题
在cdylib crate中定义loaded_fn函数修改传入的Box<fn()>指向库内私有函数时,主程序调用修改后的函数触发STATUS_ACCESS_VIOLATION段错误,以下是问题根源与解决方案:
问题根源
- 私有函数未导出:库内的
dangling函数未标记导出,编译时可能被链接器优化移除,导致运行时地址无效。 - Rust ABI跨库不兼容:
extern "Rust"是Rust内部ABI,未标准化,主程序与动态库的编译环境差异会导致ABI不匹配,引发调用栈错误。 - 直接修改Box指针的风险:直接修改
Box<fn()>的底层函数指针,相当于将动态库未暴露的符号地址传递给主程序,主程序无法正确解析该地址。
解决方案
方案1:改用C ABI并导出目标函数
C ABI是标准化的跨库调用方案,同时需要导出目标函数确保符号可见。
修改lib/src/lib.rs:
#[no_mangle] pub extern "C" fn dangling() { println!("Hello from the returned function"); } #[no_mangle] pub extern "C" fn loaded_fn(func: *mut extern "C" fn()) { unsafe { *func = dangling; } }
修改use-lib/src/main.rs:
use libloading::{Library, Symbol}; extern "C" fn default() { println!("function not modified"); } fn main() { let mut dangling_fn: extern "C" fn() = default; unsafe { type LoadedFn = extern "C" fn(*mut extern "C" fn()); let lib = Library::new("./lib.dll").unwrap_or_else(|err| { panic!("unable to load library: {}", err); }); println!("loaded lib"); let loaded_fn: Symbol<LoadedFn> = lib.get(b"loaded_fn").unwrap_or_else(|err| { panic!("unable to get symbol: {}", err); }); println!("loaded function"); loaded_fn(&mut dangling_fn); println!("called function"); } println!("calling dangling function"); dangling_fn(); }
方案2:返回Box<dyn Fn()>指针(更符合Rust风格)
让动态库返回封装好的函数对象指针,主程序接收后转换为Box,用完由库负责释放,避免跨库内存管理冲突。
修改lib/src/lib.rs:
#[no_mangle] pub extern "C" fn get_dangling() -> *mut dyn Fn() { Box::into_raw(Box::new(|| { println!("Hello from the returned function"); })) } #[no_mangle] pub extern "C" fn drop_func(func: *mut dyn Fn()) { unsafe { Box::from_raw(func); } }
修改use-lib/src/main.rs:
use libloading::{Library, Symbol}; fn main() { unsafe { type GetDanglingFn = extern "C" fn() -> *mut dyn Fn(); type DropFuncFn = extern "C" fn(*mut dyn Fn()); let lib = Library::new("./lib.dll").unwrap_or_else(|err| { panic!("unable to load library: {}", err); }); println!("loaded lib"); let get_dangling: Symbol<GetDanglingFn> = lib.get(b"get_dangling").unwrap(); let drop_func: Symbol<DropFuncFn> = lib.get(b"drop_func").unwrap(); println!("loaded functions"); let func_ptr = get_dangling(); let mut dangling_fn = Box::from_raw(func_ptr); println!("got function"); println!("calling dangling function"); dangling_fn(); drop_func(Box::into_raw(dangling_fn)); } }
关键说明
- 跨动态库传递函数/对象必须使用标准化ABI(如C ABI),避免依赖Rust内部ABI。
- 动态库中需外部访问的符号必须标记
#[no_mangle],确保链接器保留符号并加入导出表。 - 跨库传递堆分配对象时,必须由分配该对象的库负责释放,避免内存管理冲突。
内容的提问来源于stack exchange,提问作者TwiceUponATime
相关产品推荐
相关产品推荐

