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


我正在玩弄 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

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


10204
2017-11-01 10:55


起源



答案:


这里的问题是如何 symbol 功能有效。它有签名:

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

加载的库基本上是内存中的一个大数组,某些地址标有名称(符号名称)。查询符号会查找地址并直接返回指针。库中的函数是一长串指令,因此查询函数的名称会直接返回一个(函数)指针。然后可以将其称为普通函数指针。铁锈 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(...) -> ...


8
2017-11-03 05:45





我认为问题源于你在不兼容的类型之间进行投射。具体来说,取消引用 *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]

5
2017-11-02 14:27



哦,我明白了,谢谢。文档根本不清楚。 - Levans
那是非常慈善的;目前还没有任何文档...我甚至看到了 #![allow(missing_docs)] 而源头潜水^ _ ^。 - Shepmaster


答案:


这里的问题是如何 symbol 功能有效。它有签名:

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

加载的库基本上是内存中的一个大数组,某些地址标有名称(符号名称)。查询符号会查找地址并直接返回指针。库中的函数是一长串指令,因此查询函数的名称会直接返回一个(函数)指针。然后可以将其称为普通函数指针。铁锈 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(...) -> ...


8
2017-11-03 05:45





我认为问题源于你在不兼容的类型之间进行投射。具体来说,取消引用 *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]

5
2017-11-02 14:27



哦,我明白了,谢谢。文档根本不清楚。 - Levans
那是非常慈善的;目前还没有任何文档...我甚至看到了 #![allow(missing_docs)] 而源头潜水^ _ ^。 - Shepmaster


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


0
2017-08-29 07:16