You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 01:15:08