2014-11-01 92 views
10

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

而且在這裏我很迷茫。我究竟做錯了什麼 ?

回答

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的代碼開始的指針),所以它只是成爲向編譯器表達這個問題的一個問題。不幸的是,目前沒有使*mut T成爲函數指針的類型T,因此必須首先設置T = u8(即lib.symbol::<u8>("minicall")),然後通過transmute::<_, fn() -> u8>(pointer)將返回值轉換爲適當的函數指針類型。

(我甚至對方的回答被接受,因爲我不認爲這解釋了原因非常好做後回答這個問題,只是給瞭解決方案。)


最後一件事,這不是在這種情況下是一個問題,但它會讓人們觸動很多:Rust ABI(用於fn(...) -> ...類型函數的調用約定)與C ABI不同,因此從C動態庫加載的函數應該被賦予類型extern "C" fn(...) -> ...,不是fn(...) -> ...

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] 
+0

哦,我明白了,謝謝。文檔根本不清楚。 – Levans 2014-11-02 14:45:54

+0

這是非常慈善的;目前沒有真正的文檔......我甚至在source-dive^_ ^的時候看到了'#![allow(missing_docs)]'。 – Shepmaster 2014-11-02 16:40:23