Dynamiczne linkowanie - Linux vs. Windows

10

Pod Windows, kiedy kompiluję kod C / C ++ w projekcie DLL w MSVC, otrzymuję 2 pliki:

  1. MyDll.dll
  2. MyDll.lib

gdzie, o ile rozumiem, MyDll.libzawiera pewnego rodzaju tabelę wskaźników wskazującą lokalizacje funkcji w bibliotece dll. Podczas korzystania z tej biblioteki DLL, powiedzmy w pliku exe, MyDll.libjest ona osadzana w pliku exe podczas łączenia, więc w czasie wykonywania „wie”, gdzie znajdują się funkcje MyDll.dlli może z nich korzystać.

Ale jeśli skompiluję ten sam kod w systemie Linux, otrzymam tylko jeden plik MySo.sobez MySo.a(odpowiednik libpliku w systemie Linux), więc skąd plik wykonywalny w systemie Linux wie, gdzie znajdują się funkcje, MySo.sojeśli nic nie jest w nim osadzone podczas łączenia?

Benny K.
źródło

Odpowiedzi:

1

W Linuksie linker (nie dynamiczny linker) przeszukuje biblioteki współdzielone określone w czasie linkowania i tworzy odniesienia do nich w pliku wykonywalnym. Kiedy dynamiczny linker ładuje te pliki wykonywalne, ładuje wymagane biblioteki współdzielone do pamięci i rozwiązuje symbole, co pozwala na uruchamianie plików binarnych.

MySo.a, jeśli zostanie utworzony, w rzeczywistości zawierałby symbole, które mają zostać połączone bezpośrednio w pliku binarnym zamiast „tabel wyszukiwania symboli” używanych w systemie Windows.

Odpowiedź rustyx wyjaśnia proces w systemie Windows dokładniej niż potrafię; minęło dużo czasu, odkąd korzystałem z systemu Windows.

SS Anne
źródło
1
„Windows ma inne podejście ... określ w systemie operacyjnym dokładnie, gdzie symbole znajdują się w DLL” - to przeczy wiki , która mówi, że nazwy funkcji są nadal rozwiązywane (podczas uruchamiania lub przy pierwszym wywołaniu funkcji biblioteki), nawet gdy używaj porządków porządkowych (chyba że zostanie użyte bezpośrednie wiązanie adresu, czego nikt by nie zrobił, ponieważ zmusza to użytkowników biblioteki do ponownej kompilacji i ponownego wdrożenia kodu przy każdej zmianie biblioteki).
yugr
@yugr Usunąłem tę część, i tak chwytałem słomki.
SS Anne
4

Linker MSVC może łączyć ze sobą pliki obiektów (.obj) i biblioteki obiektów (.lib) w celu utworzenia pliku .EXE lub .DLL.

Aby połączyć się z DLL, proces w MSVC polega na użyciu tak zwanej biblioteki importu (.LIB), która działa jak klej między nazwami funkcji C a tabelą eksportu DLL (w DLL można wyeksportować funkcję według nazwy lub przez porządkowy - ten ostatni był często używany do nieudokumentowanych interfejsów API).

Jednak w większości przypadków tabela eksportu DLL ma wszystkie nazwy funkcji, a zatem biblioteka importu (.LIB) zawiera w dużej mierze zbędne informacje („ funkcja importu ABC -> funkcja eksportowana ABC ” itp.).
Możliwe jest nawet wygenerowanie pliku .LIB z istniejącego pliku .DLL.

Linkery na innych platformach nie mają tej „funkcji” i mogą łączyć się bezpośrednio z bibliotekami dynamicznymi.

rustyx
źródło
„Linkery na innych platformach nie mają tej funkcji” - jest jednak łatwe do wdrożenia (np. Implib.so robi to dla Linuksa), aby uzyskać opóźnione ładowanie i inne korzyści.
yugr
@yugr: dlatego „funkcja” jest w cudzysłowie - nie jest to coś, co na ogół chcesz robić i to dodatkowa praca, którą musisz wykonać w systemie Windows.
Chris Dodd
1

Różnica, którą widzisz, to bardziej szczegół implementacji - pod maską zarówno Linux, jak i Windows działają podobnie - kod wywołuje funkcję kodu pośredniczącego, który jest statycznie powiązany w pliku wykonywalnym, a ten kod pośredniczący ładuje w razie potrzeby bibliotekę DLL / shlib (w przypadku opóźnienia ładowanie , w przeciwnym razie biblioteka jest ładowana przy uruchomieniu programu) i (przy pierwszym wywołaniu) rozwiązuje symbol przez GetProcAddress/ dlsym.

Jedyna różnica polega na tym, że w systemie Linux te kody pośredniczące (zwane kodami pośredniczącymi PLT) są generowane dynamicznie po połączeniu aplikacji z biblioteką dynamiczną (biblioteka zawiera wystarczającą ilość informacji do ich wygenerowania), podczas gdy w systemie Linux są one generowane, gdy sama biblioteka DLL jest utworzony w osobnym .libpliku.

Te dwa podejścia są tak podobne, że w rzeczywistości można naśladować biblioteki Windows importujące biblioteki w systemie Linux (patrz projekt Implib.so ).

yugr
źródło
0

W Linuksie przekazujesz MySo.solinkerowi, który jest w stanie wyodrębnić tylko to, co jest potrzebne do fazy łącza, wprowadzając odwołanie MySo.sopotrzebne w czasie wykonywania.

AProgrammer
źródło
-3

.dlllub .sosą udostępnionymi bibliotekami lib (połączonymi w czasie wykonywania), podczas gdy .ai.lib są biblioteką statyczną (połączoną w czasie kompilacji). Nie ma różnicy między Windows i Linux.

Różnica polega na tym, w jaki sposób są obsługiwane. Uwaga: różnica dotyczy tylko organów celnych, w jaki sposób są one wykorzystywane. Nie byłoby trudno stworzyć Linuksa w systemie Windows i odwrotnie, z tym że praktycznie nikt tego nie robi.

Jeśli używamy biblioteki dll lub wywołujemy funkcję nawet z własnego pliku binarnego, istnieje prosty i przejrzysty sposób. Na przykład w C widzimy, że:

int example(int x) {
  ...do_something...
}

int ret = example(42);

Jednak na poziomie asm może być wiele różnic. Na przykład na x86 callwykonywany jest kod operacji, który 42jest podawany na stosie. Lub w niektórych rejestrach. Lub gdziekolwiek. Nikt nie wie, że przed napisaniem biblioteki dll , w jaki sposób będzie ona używana. Lub w jaki sposób projekty będą chciały z niego korzystać, możliwe napisane przy pomocy kompilatora (lub w języku!), Który nawet nie istnieje (lub jest nieznany twórcom dll).

Na przykład domyślnie zarówno C, jak i Pascal umieszczają argumenty (i uzyskują wartości zwracane) ze stosu - ale robią to w innej kolejności . Możesz także wymieniać argumenty między funkcjami w rejestrach przez optymalizację zależną od kompilatora.

Jak widzisz poprawnie, niestandardowy system Windows polega na tym, że budując bibliotekę DLL, tworzymy również minimalny .a / .libza jej pomocą. Ta minimalna statyczna biblioteka jest tylko opakowaniem, poprzez które można uzyskać dostęp do symboli (funkcji) tej biblioteki DLL. To sprawia, że ​​wymagane konwersje telefoniczne na poziomie asm.

Jego zaletą jest kompatybilność. Jego wadą jest to, że jeśli masz tylko plik .dll, możesz mieć trudności z ustaleniem, jak mają być wywoływane jego funkcje. To sprawia, że ​​korzystanie z bibliotek DLL jest zadaniem hakującym, bibliotek jeśli programista biblioteki DLL nie daje takiej możliwości.a . Służy więc głównie celom związanym z zamknięciem, na przykład łatwiej jest zdobyć dodatkową gotówkę na zestawy SDK.

Jego kolejną wadą jest to, że nawet jeśli używasz biblioteki dynamicznej, musisz skompilować to małe opakowanie statycznie.

W Linuksie interfejs binarny bibliotek DLL jest standardem i jest zgodny z konwencją C. Tak więc nie .ajest wymagane i istnieje zgodność binarna między udostępnionymi bibliotekami, w zamian nie mamy zalet niestandardowego oprogramowania Microsoft.

peterh - Przywróć Monikę
źródło
1
Podaj link potwierdzający, że funkcje kodu pośredniczącego mogą zmieniać kolejność argumentów. Nigdy wcześniej o tym nie słyszałem i trudno w to uwierzyć, biorąc pod uwagę, jak duży byłby narzut związany z wydajnością.
yugr
@yugr Prosta zmiana kolejności rejestrów / stosów nie stanowi narzutu wydajności. Jeśli używasz bibliotek DLL skompilowanych z msvc z plików binarnych skompilowanych z msvc, to oczywiście nie wydarzy się zbyt wiele, ale może.
peterh - Przywróć Monikę
1
Moglibyśmy się o to kłócić, ale jeśli masz rację, powinno być łatwe zapewnienie dowodu, że funkcje kodu pośredniczącego są w stanie trywialnym przetwarzaniu argumentów (i być czymś więcej niż tylko sztucznymi trampolinami).
yugr
@yugr Kody pośredniczące mają dostęp do sygnatur funkcji biblioteki dll, co sprawia, że ​​niebanalne przetwarzanie jest banalne.
peterh - Przywróć Monikę
1
Sugeruję jedynie uzupełnienie odpowiedzi kilkoma linkami dowodowymi dotyczącymi tego, co robi biblioteka importu (ponieważ niektóre twierdzenia są wątpliwe).
yugr