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(...) -> ...
.
O, widzę, dzięki. Dokumentacja w ogóle o tym nie mówi. – Levans
To bardzo charytatywne; tak naprawdę nie ma w tej chwili żadnych dokumentów ... Widziałem nawet # #! [allow (missing_docs)] 'while diving diving^_ ^. – Shepmaster