2014-11-01 21 views
10

Tôi hiện đang phát xung quanh với DynamicLibrary.Gọi theo cách thủ công thư viện động gỉ

Mã của thư viện năng động của tôi (biên soạn với rustc --crate-type dylib dylib.rs):

// dylib.rs 
#[no_mangle] 
pub fn minicall() -> u8 { 
    3u8 
} 

Và mã để gọi nó là:

// 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); 
} 

Tôi có đầu ra này:

~> ./caller 
Unsafe bloc ! 
call func ! 
Illegal instruction 

Và ở đây tôi khá lạc. Tôi đang làm gì sai?

Trả lời

8

Vấn đề ở đây là cách symbol chức năng hoạt động. Chữ ký có chữ ký:

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

Thư viện được tải về cơ bản là một mảng lớn trong một số địa chỉ nhất định có gắn nhãn (tên biểu tượng). Truy vấn biểu tượng tìm kiếm địa chỉ và trả về một con trỏ thẳng đến nó. Một hàm trong thư viện là một chuỗi các lệnh dài, do đó việc truy vấn tên của hàm trả về một con trỏ (hàm) trực tiếp đến đầu. Điều này sau đó có thể được gọi là một con trỏ hàm bình thường. API Rust DynamicLibrary trả về con trỏ này, tức là, *mut T trỏ trực tiếp vào đoạn bộ nhớ trong thư viện động (được cho là/hy vọng là loại T).

Loại fn(...) -> ... là một con trỏ hàm chính nó, nghĩa là, nó là 8 byte (hoặc 4 byte) lưu trữ địa chỉ bắt đầu của hàm mà nó đại diện. Do đó, gọi số lib.symbol::< fn() -> u8 >("minicall") là nói "tìm cho tôi địa chỉ có tên là minicall (là con trỏ đến hàm)", không phải là "tìm cho tôi địa chỉ của thứ gọi là minicall (là hàm)" . Giá trị trả về của *mut (fn() -> u8) sau đó là gián tiếp gấp đôi, và dereferencing nó gọi nó là giải thích 8 (hoặc 4) byte đầu tiên của mã chức năng như một con trỏ (tức là chỉ dẫn máy ngẫu nhiên/chức năng prelude), nó không thực hiện chúng.

(Side-lưu ý:. Nó có lẽ sẽ làm việc nếu bạn có #[no_mangle] pub static minicall: fn() -> u8 = the_real_minicall; trong thư viện của bạn, nhưng có lẽ bạn không muốn điều này)

Các cuộc gọi đến lib.symbol::<T>("minicall") đang trở lại con trỏ chức năng chính xác chúng ta muốn (có nghĩa là , nó sẽ trả về một con trỏ để bắt đầu mã số minicall), do đó, nó chỉ trở thành một câu hỏi thể hiện điều này với trình biên dịch. Rất tiếc, hiện không có loại T nào làm cho *mut T con trỏ hàm, vì vậy trước tiên, bạn phải đặt T = u8 (ví dụ: lib.symbol::<u8>("minicall")) và sau đó truyền giá trị trả lại cho loại con trỏ chức năng thích hợp qua transmute::<_, fn() -> u8>(pointer).

(tôi trả lời này ngay cả sau khi câu trả lời khác đã được chấp nhận bởi vì tôi không nghĩ rằng nó giải thích nguyên nhân rất tốt, chỉ cần đưa ra các giải pháp.)


Điều cuối cùng, đây không phải là một vấn đề trong trường hợp này, nhưng nó đi rất nhiều người: Rust ABI (quy ước gọi được sử dụng cho các chức năng kiểu fn(...) -> ...) không giống với C ABI, do đó các hàm được tải từ thư viện động C phải được nhập extern "C" fn(...) -> ..., không phảifn(...) -> ....

5

Tôi nghĩ rằng vấn đề bắt nguồn từ thực tế là bạn đang truyền giữa các loại không tương thích. Cụ thể, sự thiếu cân nhắc *f sẽ trỏ đến địa điểm sai. Tôi nhìn vào mã Rust để xem cách thư viện được sử dụng và tìm thấy một ví dụ trong src/librustc/plugin/load.rs. Tôi thích đoạn mã đó để dụ của bạn:

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(); 

Sản lượng:

$ ./caller 
Unsafe bloc ! 
call func ! 
extend vec ! 
v is: [3] 
+0

Ồ, tôi hiểu rồi, cảm ơn. Các tài liệu không rõ ràng về điều này cả. – Levans

+0

Điều đó rất từ ​​thiện; không có bất kỳ tài liệu nào vào lúc này ... Tôi thậm chí còn thấy '#! [allow (missing_docs)]' trong khi source-diving^_ ^. – Shepmaster

Các vấn đề liên quan