我目前正在玩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
在这里,我很失落。我做错了什么?
答案 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上动态加载库的最流行方法。