问题 手动调用一个生锈的动态库
我正在玩弄 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
答案:
这里的问题是如何 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
既然这个问题/答案,那么 std::dynamic_lib
API似乎已经消失了。在撰写本文时,它看起来像 libloading 是在crates.io上动态加载库的最流行的方法。
0
2017-08-29 07:16