Lev*_*ans 11 dynamic-library rust
我正在玩弄DynamicLibrary.
我的动态库的代码(编译rustc --crate-type dylib dylib.rs):
// dylib.rs
#[no_mangle]
pub fn minicall() -> u8 {
3u8
}
Run Code Online (Sandbox Code Playgroud)
以及调用它的代码:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
我有这个输出:
~> ./caller
Unsafe bloc !
call func !
Illegal instruction
Run Code Online (Sandbox Code Playgroud)
在这里,我很失落.我究竟做错了什么 ?
这里的问题是symbol函数的工作原理.它有签名:
unsafe fn symbol<T>(&self, symbol: &str) -> Result<*mut T, String>
Run Code Online (Sandbox Code Playgroud)
加载的库基本上是内存中的一个大数组,某些地址标有名称(符号名称).查询符号会查找地址并直接返回指针.库中的函数是一长串指令,因此查询函数的名称会直接返回一个(函数)指针.然后可以将其称为普通函数指针.Rust DynamicLibraryAPI返回这个指针,即*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(...) -> ....
我认为问题源于你在不兼容的类型之间进行投射.具体来说,取消引用*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();
Run Code Online (Sandbox Code Playgroud)
输出:
$ ./caller
Unsafe bloc !
call func !
extend vec !
v is: [3]
Run Code Online (Sandbox Code Playgroud)