Wiem, że to może wydawać się dość proste dla maniaków. Ale chcę, żeby było to krystalicznie jasne.
Kiedy chcę użyć biblioteki DLL Win32, zwykle po prostu wywołuję interfejsy API, takie jak LoadLibrary () i GetProcAdderss (). Ale ostatnio rozwijam się z DirectX9 i muszę dodać pliki d3d9.lib , d3dx9.lib itp.
Słyszałem wystarczająco dużo, że LIB służy do łączenia statycznego, a DLL do łączenia dynamicznego.
Dlatego obecnie rozumiem, że LIB zawiera implementację metod i jest statycznie łączony w czasie łączenia jako część końcowego pliku EXE. Biblioteka DLL jest dynamicznie ładowana w czasie wykonywania i nie jest częścią końcowego pliku EXE.
Ale czasami z plikami DLL są dostarczane pliki LIB , więc:
- Do czego służą te pliki LIB?
- Jak osiągają to, do czego są przeznaczone?
- Czy są jakieś narzędzia, które pozwolą mi sprawdzić wewnętrzne elementy tych plików LIB?
Zaktualizuj 1
Po sprawdzeniu wikipedii pamiętam, że te pliki LIB nazywają się biblioteką importu . Ale zastanawiam się, jak działa dynamiczne ładowanie mojej głównej aplikacji i bibliotek DLL.
Zaktualizuj 2
Tak jak powiedział RBerteig, w plikach LIB utworzonych z bibliotekami DLL znajduje się kod pośredniczący. Więc sekwencja wywołania powinna wyglądać następująco:
Moja główna aplikacja -> odgałęzienie w LIB -> prawdziwa docelowa biblioteka DLL
Więc jakie informacje powinny być zawarte w tych LIB? Mogę pomyśleć o następujących rzeczach:
- Plik LIB powinien zawierać pełną ścieżkę odpowiedniej biblioteki DLL; Zatem biblioteka DLL może zostać załadowana przez środowisko wykonawcze.
- Względny adres (lub przesunięcie pliku?) Punktu wejścia każdej metody eksportu DLL powinien być zakodowany w kodowaniu; Więc można było wykonać poprawne skoki / wywołania metod.
Czy mam rację? Czy jest coś więcej?
BTW: Czy jest jakieś narzędzie, które może sprawdzić bibliotekę importu? Jeśli to zobaczę, nie będzie więcej wątpliwości.
źródło
lib /list xxx.lib
ilink /dump /linkermember xxx.lib
. Zobacz to pytanie o przepełnienie stosu .dumpbin -headers xxx.lib
zapewnia bardziej szczegółowe informacje, w porównaniu dolib
ilink
użyteczności publicznej.Odpowiedzi:
Łączenie z plikiem DLL może nastąpić niejawnie w czasie łączenia
kompilacjilub jawnie w czasie wykonywania. Tak czy inaczej, biblioteka DLL zostaje załadowana do przestrzeni pamięci procesów, a wszystkie jej wyeksportowane punkty wejścia są dostępne dla aplikacji.Jeśli jest używany jawnie w czasie wykonywania, możesz użyć
LoadLibrary()
iGetProcAddress()
ręcznie załadować bibliotekę DLL i uzyskać wskaźniki do funkcji, które musisz wywołać.Jeśli program jest połączony niejawnie podczas budowania programu, wówczas kody pośredniczące dla każdego eksportu DLL używanego przez program są łączone z programem z biblioteki importu, a te kody pośredniczące są aktualizowane, gdy pliki EXE i DLL są ładowane po uruchomieniu procesu. (Tak, tutaj uprościłem więcej niż trochę ...)
Te kody pośredniczące muszą skądś pochodzić, aw łańcuchu narzędzi firmy Microsoft pochodzą ze specjalnej formy pliku .LIB zwanej biblioteką importu . Wymagana biblioteka .LIB jest zwykle budowana w tym samym czasie co biblioteka DLL i zawiera kod pośredniczący dla każdej funkcji eksportowanej z biblioteki DLL.
Co dziwne, statyczna wersja tej samej biblioteki byłaby również dostarczana jako plik .LIB. Nie ma prostego sposobu na ich rozróżnienie, z wyjątkiem tego, że biblioteki LIB, które są bibliotekami importowanymi dla bibliotek DLL, będą zwykle mniejsze (często znacznie mniejsze) niż pasująca statyczna biblioteka LIB.
Jeśli używasz zestawu narzędzi GCC, nawiasem mówiąc, nie potrzebujesz bibliotek importu, aby pasowały do twoich bibliotek DLL. Wersja linkera Gnu przeportowana do Windows bezpośrednio rozumie biblioteki DLL i może zsyntetyzować większość wymaganych kodów pośredniczących w locie.
Aktualizacja
Jeśli po prostu nie możesz się oprzeć wiedzy, gdzie naprawdę są wszystkie śruby i nakrętki i co się naprawdę dzieje, w MSDN zawsze jest coś, co może Ci pomóc. Artykuł Matta Pietreka Dogłębne spojrzenie na format przenośnego pliku wykonywalnego Win32 to bardzo kompletny przegląd formatu pliku EXE oraz tego, jak jest ładowany i uruchamiany. Został nawet zaktualizowany, aby objąć .NET i więcej, odkąd pojawił się w MSDN Magazine ca. 2002.
Pomocne może być również poznanie, jak dokładnie dowiedzieć się, jakie biblioteki DLL są używane przez program. Narzędziem służącym do tego jest Dependency Walker, znany również jako depend.exe. Jego wersja jest dołączona do programu Visual Studio, ale najnowsza wersja jest dostępna u jej autora pod adresem http://www.dependencywalker.com/ . Potrafi zidentyfikować wszystkie biblioteki DLL, które zostały określone w czasie połączenia (zarówno wczesne ładowanie, jak i ładowanie z opóźnieniem), a także może uruchomić program i obserwować wszelkie dodatkowe biblioteki DLL, które ładuje w czasie wykonywania.
Zaktualizuj 2
Przeformułowałem niektóre z wcześniejszych tekstów, aby wyjaśnić go przy ponownym czytaniu i aby użyć terminów sztuki ukrytych i jawnych w celu zachowania spójności z MSDN.
Mamy więc trzy sposoby udostępnienia funkcji bibliotecznych do użycia przez program. Oczywiste pytanie uzupełniające brzmi zatem: „Jak wybrać drogę?”
Łączenie statyczne polega na łączeniu większości samego programu. Wszystkie pliki obiektowe są wyświetlane na liście i są gromadzone razem w pliku EXE przez konsolidator. Po drodze linker zajmuje się drobnymi obowiązkami, takimi jak naprawianie odwołań do symboli globalnych, aby moduły mogły wywoływać nawzajem funkcje. Biblioteki można również łączyć statycznie. Pliki obiektowe, które tworzą bibliotekę, są gromadzone razem przez bibliotekarza w pliku .LIB, który konsolidator wyszukuje moduły zawierające potrzebne symbole. Jednym ze skutków łączenia statycznego jest to, że tylko te moduły z biblioteki, które są używane przez program, są z nią połączone; inne moduły są ignorowane. Na przykład tradycyjna biblioteka matematyczna C zawiera wiele funkcji trygonometrycznych. Ale jeśli połączysz się z nim i użyjesz
cos()
, nie otrzymasz kopii kodu dlasin()
lub otan()
ile nie wywołałeś również tych funkcji. W przypadku dużych bibliotek z bogatym zestawem funkcji ważne jest to selektywne włączanie modułów. Na wielu platformach, takich jak systemy wbudowane, całkowity rozmiar kodu dostępnego do wykorzystania w bibliotece może być duży w porównaniu z przestrzenią dostępną do przechowywania pliku wykonywalnego w urządzeniu. Bez selektywnego włączania trudniej byłoby zarządzać szczegółami tworzenia programów dla tych platform.Jednak posiadanie kopii tej samej biblioteki w każdym uruchomionym programie stanowi obciążenie dla systemu, który zwykle wykonuje wiele procesów. Przy odpowiednim systemie pamięci wirtualnej strony pamięci o identycznej zawartości muszą istnieć tylko raz w systemie, ale mogą być używane przez wiele procesów. Stwarza to korzyść w postaci zwiększenia prawdopodobieństwa, że strony zawierające kod będą prawdopodobnie identyczne z niektórymi stronami w tak wielu innych uruchomionych procesach, jak to tylko możliwe. Ale jeśli programy statycznie łączą się z biblioteką wykonawczą, to każdy z nich ma inną kombinację funkcji, z których każda jest rozmieszczona w celu przetwarzania mapy pamięci w różnych lokalizacjach i nie ma wielu współdzielonych stron kodowych, chyba że jest to program, który sam w sobie jest działać w czymś więcej niż tylko proces. Tak więc pomysł biblioteki DLL zyskał kolejną, główną zaletę.
Biblioteka DLL dla biblioteki zawiera wszystkie jej funkcje, gotowe do użycia przez dowolny program kliencki. Jeśli wiele programów ładuje tę bibliotekę DLL, wszystkie mogą udostępniać jej strony kodowe. Wszyscy wygrywają. (Cóż, dopóki nie zaktualizujesz biblioteki DLL nową wersją, ale to nie jest część tej historii. Google DLL Piekło po tej stronie historii).
Tak więc pierwszy duży wybór podczas planowania nowego projektu dotyczy połączenia dynamicznego i statycznego. Dzięki statycznemu łączeniu masz mniej plików do zainstalowania i jesteś odporny na aktualizację używanej biblioteki DLL przez osoby trzecie. Jednak twój program jest większy i nie jest tak dobrym obywatelem ekosystemu Windows. Dzięki dynamicznemu łączeniu masz więcej plików do zainstalowania, możesz mieć problemy z aktualizacją używanej biblioteki DLL przez inną firmę, ale generalnie jesteś bardziej przyjazny dla innych procesów w systemie.
Dużą zaletą biblioteki DLL jest to, że można ją załadować i używać bez ponownej kompilacji lub nawet ponownego łączenia głównego programu. Może to pozwolić zewnętrznemu dostawcy biblioteki (na przykład Microsoftowi i środowisku wykonawczemu C) naprawić błąd w swojej bibliotece i rozpowszechnić go. Gdy użytkownik końcowy zainstaluje zaktualizowaną bibliotekę DLL, natychmiast skorzysta z tej poprawki błędu we wszystkich programach, które używają tej biblioteki DLL. (Chyba że coś zepsuje. Zobacz DLL Hell).
Druga zaleta wynika z rozróżnienia między niejawnym i jawnym ładowaniem. Jeśli podejmiesz dodatkowy wysiłek w postaci jawnego ładowania, biblioteka DLL może nawet nie istnieć, gdy program został napisany i opublikowany. Pozwala to na mechanizmy rozszerzeń, które mogą na przykład wykrywać i ładować wtyczki.
źródło
Te pliki bibliotek importu .LIB są używane w poniższej właściwości projektu
Linker->Input->Additional Dependencies
, podczas budowania zestawu bibliotek dll, które wymagają dodatkowych informacji w czasie łączenia, które są dostarczane przez pliki .LIB biblioteki importu. W poniższym przykładzie, aby nie uzyskać błędów konsolidatora, muszę odwoływać się do bibliotek dll A, B, C i D za pośrednictwem ich plików lib. (uwaga dla konsolidatora, aby znaleźć te pliki, może być konieczne dołączenie ich ścieżki wdrażania, w przeciwnymLinker->General->Additional Library Directories
razie zostanie wyświetlony błąd kompilacji dotyczący niemożności znalezienia żadnego z dostarczonych plików lib).Jeśli Twoje rozwiązanie buduje wszystkie biblioteki dynamiczne, być może udało Ci się uniknąć tej jawnej specyfikacji zależności, polegając zamiast tego na flagach referencyjnych ujawnionych w
Common Properties->Framework and References
oknie dialogowym. Te flagi wydają się automatycznie tworzyć łącza w Twoim imieniu przy użyciu plików * .lib.Jest to jednak tak, jak mówi, Właściwości wspólne , które nie są specyficzne dla konfiguracji ani platformy. Jeśli potrzebujesz obsługiwać scenariusz kompilacji mieszanej, tak jak w naszej aplikacji, mieliśmy konfigurację kompilacji do renderowania kompilacji statycznej i specjalną konfigurację, która zbudowała ograniczoną kompilację podzbioru zestawów, które zostały wdrożone jako biblioteki dynamiczne. Użyłem flag
Use Library Dependency Inputs
iLink Library Dependencies
ustawionych na true w różnych przypadkach, aby uzyskać rzeczy do zbudowania, a później uświadomiłem sobie, aby uprościć rzeczy, ale wprowadzając mój kod do kompilacji statycznych, wprowadziłem mnóstwo ostrzeżeń linkera, a kompilacja była niesamowicie powolna w przypadku kompilacji statycznych. Skończyło się na wprowadzeniu kilku tego rodzaju ostrzeżeń ...warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored D.lib(JSource.obj)
Skończyło się na tym, że użyłem ręcznej specyfikacji programu,
Additional Dependencies
aby zadowolić konsolidator dla dynamicznych kompilacji, a jednocześnie zachować zadowolenie statycznych budowniczych, nie używając wspólnej właściwości, która je spowalnia. Kiedy wdrażam kompilację podzbioru dynamicznego, wdrażam tylko pliki dll, ponieważ te pliki lib są używane tylko w czasie łączenia, a nie w czasie wykonywania.źródło
Istnieją trzy rodzaje bibliotek: biblioteki statyczne, współdzielone i ładowane dynamicznie.
Biblioteki statyczne są połączone z kodem na etapie łączenia, więc w rzeczywistości znajdują się w pliku wykonywalnym, w przeciwieństwie do biblioteki współdzielonej, która ma tylko kody pośredniczące (symbole) do wyszukania w pliku biblioteki współdzielonej, który jest ładowany w czasie wykonywania przed wywoływana jest funkcja główna.
Te ładowane dynamicznie są bardzo podobne do bibliotek współdzielonych, z wyjątkiem tego, że są ładowane, gdy i jeśli taka potrzeba wynika z napisanego kodu.
źródło
LoadLibrary()
i powiązane interfejsy API.Oto kilka powiązanych tematów MSDN, aby odpowiedzieć na moje pytanie:
Łączenie pliku wykonywalnego z biblioteką DLL
Łączenie niejawne
Określanie, której metody łączenia użyć
Tworzenie biblioteki importu i pliku eksportu
źródło