2014-11-01 11 views
10

Aktualnie używam wersji DynamicLibrary.Ręcznie wywołaj bibliotekę dynamiczną z rdzą

Kod mojej biblioteki dynamicznej (skompilowany z rustc --crate-type dylib dylib.rs):

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

I kod to nazwać:

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

mam ten wynik:

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

I tutaj jestem całkiem zagubiony. Co ja robię źle ?

Odpowiedz

8

Problem polega na tym, w jaki sposób działa funkcja symbol. Ma Podpis:

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

załadowany biblioteka jest w zasadzie wielka tablica w pamięci z niektórych adresów oznaczonych nazwą (nazwami Symbol). Zapytanie o symbol wyszukuje adres i zwraca wskaźnik bezpośrednio do niego. Funkcja w bibliotece to długa sekwencja instrukcji, więc zapytanie o nazwę funkcji zwraca wskaźnik (funkcji) bezpośrednio na początek. Można to nazwać normalnym wskaźnikiem funkcji. Interfejs API Rust DynamicLibrary zwraca ten wskaźnik, czyli *mut T, bezpośrednio do fragmentu pamięci w bibliotece dynamicznej (która prawdopodobnie ma/jest typem T).

Typ fn(...) -> ... to sam wskaźnik funkcji, to jest 8 bajtów (lub 4 bajty) przechowujących adres początkowy funkcji, którą reprezentuje. Dlatego wywołanie lib.symbol::< fn() -> u8 >("minicall") mówi "znajdź mi adres rzeczy o nazwie minicall (która jest wskaźnikiem do funkcji)", to nie jest powiedzenie "znajdź mi adres rzeczy o nazwie minicall (która jest funkcją)" . Wartość zwracana wynosząca *mut (fn() -> u8) jest wówczas podwójnie pośrednia, a wybranie jej wywołania powoduje interpretację pierwszych 8 (lub 4) bajtów kodu funkcji jako wskaźnika (tj. Losowe instrukcje maszynowe/preludium funkcji), ale ich nie wykonuje.

(Side-note. Będzie to prawdopodobnie działa, jeśli miał #[no_mangle] pub static minicall: fn() -> u8 = the_real_minicall; w bibliotece, ale prawdopodobnie nie chce)

wywołanie lib.symbol::<T>("minicall") wraca dokładną funkcję wskaźnika chcemy (czyli , zwraca wskaźnik do początku kodu minicall), więc staje się po prostu kwestią wyrażenia tego kompilatorowi. Niestety obecnie nie ma typu T, który powoduje, że *mut T jest wskaźnikiem funkcji, więc najpierw należy ustawić T = u8 (tj. lib.symbol::<u8>("minicall")), a następnie przesłać wartość zwracaną do odpowiedniego typu wskaźnika po transmute::<_, fn() -> u8>(pointer).

(mam odpowiedzi na to nawet po drugiej odpowiedź została zaakceptowana, bo nie sądzę, żeby to wyjaśnić przyczynę bardzo dobrze, po prostu dał rozwiązanie.)


Ostatnią rzeczą, to nie jest problem w tym przypadku, ale bardzo dużo ludzi podróżuje: Rust ABI (konwencja wywołująca dla funkcji typu fn(...) -> ...) nie jest taka sama jak C ABI, więc funkcje ładowane z bibliotek dynamicznych C powinny mieć typ: extern "C" fn(...) -> ..., niefn(...) -> ....

5

Myślę, że problem wynika z faktu, że rzucasz między niekompatybilnymi typami. W szczególności dereferencja *f wskaże niewłaściwe miejsce. Sprawdziłem kod Rust, aby zobaczyć, jak biblioteka ma być używana i znalazłem przykład w src/librustc/plugin/load.rs. I ten kod dostosowany do przykładu:

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

dane wyjściowe:

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

O, widzę, dzięki. Dokumentacja w ogóle o tym nie mówi. – Levans

+0

To bardzo charytatywne; tak naprawdę nie ma w tej chwili żadnych dokumentów ... Widziałem nawet # #! [allow (missing_docs)] 'while diving diving^_ ^. – Shepmaster