手动调用一个生锈的动态库

时间:2014-11-01 10:55:59

标签: rust dynamic-library

我目前正在玩DynamicLibrary

我的动态库的代码(使用rustc --crate-type dylib dylib.rs编译):

// dylib.rs
#[no_mangle]
pub fn minicall() -> u8 {
    3u8
}

调用它的代码:

// caller.rs
use std::dynamic_lib::DynamicLibrary;

fn main() {
    let mut v = Vec::new();

    DynamicLibrary::prepend_search_path(&::std::os::getcwd());

    match DynamicLibrary::open(Some("./libdylib.so")) {
        Err(e) => panic!("ERROR: {}", e),
        Ok(lib) => {
            println!("Unsafe bloc !");
            let func = unsafe {
                match lib.symbol::< fn() -> u8 >("minicall") {
                        Err(e) => { panic!("ERROR: {}", e) },
                        Ok(f) => { *f },
                }
            };
            println!("call func !");
            let new_value = func();

            println!("extend vec !");
            v.push(new_value);
        }
    }

    println!("v is: {}", v);
}

我有这个输出:

~> ./caller 
Unsafe bloc !
call func !
Illegal instruction

在这里,我很失落。我做错了什么?

3 个答案:

答案 0 :(得分:8)

这里的问题是symbol函数的工作原理。它有签名:

unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String>

加载的库基本上是内存中的一个大数组,某些地址标有名称(符号名称)。查询符号会查找地址并直接返回指针。库中的函数是一长串指令,因此查询函数的名称会直接返回一个(函数)指针。然后可以将其称为普通函数指针。 Rust DynamicLibrary API返回此指针,即*mut T直接指向动态库中的内存块(据推测/希望类型为T)。

类型fn(...) -> ...是一个函数指针本身,也就是说,它是8个字节(或4个字节),存储它所代表的函数的起始地址。因此,调用lib.symbol::< fn() -> u8 >("minicall")说“找到我称为minicall的东西的地址(这是一个指向函数的指针)”,它并没有说“找到我名为minicall的东西的地址(这是一个函数)“。然后*mut (fn() -> u8)的返回值是双重间接的,并且取消引用它来调用它是将函数代码的前8个(或4个)字节解释为指针(即随机机器指令/函数前奏),它是没有执行它们。

(旁注:如果您的库中有#[no_mangle] pub static minicall: fn() -> u8 = the_real_minicall;,它可能会有效,但您可能不希望这样。)

lib.symbol::<T>("minicall")的调用返回了我们想要的确切函数指针(也就是说,它返回一个指向minicall代码开头的指针),所以它只是一个表达的问题这到编译器。不幸的是,目前没有类型T使*mut T成为函数指针,因此必须首先设置T = u8(即lib.symbol::<u8>("minicall")),然后将返回值转换为适当的值函数指针类型通过transmute::<_, fn() -> u8>(pointer)

(即使在接受了另一个答案之后我也回答了这个问题,因为我认为它没有很好地解释原因,只是给出了解决方案。)


最后一点,在这种情况下这不是问题,但它会让人们大开眼界:Rust ABI(用于类型fn(...) -> ...的函数的调用约定)与C ABI不同,所以从C动态库加载的函数应该是extern "C" fn(...) -> ...类型,不是 fn(...) -> ...

答案 1 :(得分:5)

我认为问题源于你在不兼容的类型之间进行投射。具体来说,取消引用*f将指向错误的地方。我查看了Rust代码,看看应该如何使用库,并在src/librustc/plugin/load.rs中找到了一个示例。我将该代码改编为您的示例:

let func = unsafe {
    // Let this return a `*mut u8`, a very generic pointer
    match lib.symbol("minicall") {
        Err(e) => { fail!("ERROR: {}", e) },
        // And then cast that pointer a function
        Ok(f) => { std::mem::transmute::<*mut u8, fn() -> u8>(f) },
    }
};
println!("call func !");
let new_value = func();

输出:

$ ./caller
Unsafe bloc !
call func !
extend vec !
v is: [3]

答案 2 :(得分:0)

自从这个问题/答案以来,std::dynamic_lib API似乎已经消失了。在撰写本文时,libloading似乎是在crates.io上动态加载库的最流行方法。