Piekło DLL

Z Wikipedii, wolnej encyklopedii
Skocz do: nawigacji, wyszukiwania

Piekło DLL (ang. DLL Hell) – termin w informatyce na komplikacje, które pojawiają się przy korzystaniu z bibliotek dynamicznych stosowanych w systemach operacyjnych Microsoft Windows[1], szczególnie w odziedziczonej wersji 16-bitowej, w której wszystkie aplikacje działają we wspólnej przestrzeni adresowej. Chociaż wyrażenie to jest specyficzne dla Windows, i od niego pochodzi bardziej ogólny i niezależny od platformy termin „piekło zależności”, to rym „DLL hell” w wersji angielskiej powoduje, że jego użycie jest popularne przy omawianiu ogólnych przypadków problemów zależności w Windows.

Piekło DLL może objawiać się na różne sposoby; zwykle aplikacje się nie uruchamiają lub działają nieprawidłowo. W nowych wersjach Windows problem nie jest już tak dotkliwy dzięki wprowadzeniu .NET Framework, Registration-Free COM i funkcjonalności zapobiegającej nadpisaniu plików systemowych.

Problemy[edytuj | edytuj kod]

Istnieje wiele powszechnie spotykanych problemów z bibliotekami DLL – zwłaszcza gdy w systemie zainstalowano i odinstalowano wiele różnych aplikacji. Trudności zawierają konflikty pomiędzy różnymi wersjami bibliotek DLL, problemy w dostępie do wymaganych bibliotek i istnienie wielu niepotrzebnych kopii bibliotek.

Niekompatybilne wersje[edytuj | edytuj kod]

Konkretna wersja biblioteki może być zgodna częścią (i niezgodna z inną) programów, które jej wymagają. System Windows był szczególnie podatny na tę przypadłość ze względu na nacisk do stosowania łączenia dynamicznego bibliotek C++ i mechanizm osadzania obiektów (OLE). Klasy w C++ eksportują wiele metod, a jedna zmiana w klasie (jak np. nowa metoda wirtualna) może spowodować utratę kompatybilności z programami, które były utworzone w oparciu o wcześniejszą wersję. Mechanizm osadzania obiektów ma ścisłe zasady, które mają temu zapobiec – od interfejsów wymaga się aby były stabilne a zarządzanie pamięcią nie jest współdzielone. Lecz to nie jest wystarczające, bo może się zmienić semantyka klasy. Co dla jednego programu jest „poprawką” w innym może oznaczać usunięcie „funkcjonalności”. Zanim pojawił się Windows 2000, systemy Windows były bardzo wrażliwe na takie zmiany, gdyż tabela klas COM była wspólna dla wszystkich użytkowników i procesów. Tylko jeden obiekt COM, z jednego pliku DLL/EXE mógł być zadeklarowany w systemie jako serwis dla konkretnej globalnej klasy COM. Jeśli jakikolwiek program musiał utworzyć instancję tej klasy, uzyskiwał obiekt z aktualnie zarejestrowanej implementacji. W efekcie, zainstalowanie programu, który uaktualniał wersję wspólnych bibliotek mógł nieodwracalnie uszkodzić inne programy, które były zainstalowane wcześniej.

Nieprawidłowa rejestracja COM[edytuj | edytuj kod]

W COM i innych częściach Windows, zanim wprowadzono biblioteki nie wymagające rejestracji[2], do określenia wymaganych bibliotek był używany rejestr. Jeśli zarejestrowano inną wersje modułu, to jego biblioteka będzie załadowana zamiast oczekiwanej. Taki scenariusz jest spowodowany konfliktem instalacji programów, które rejestrują różne wersje tej samej biblioteki, i w takim wypadku ostatnia instalacja decyduje o zainstalowanej wersji.

Moduły współdzielone w pamięci[edytuj | edytuj kod]

Wersja 16-bitowa Windows (oraz Windows w Windows) ładuje tylko jedną instancję zadanej biblioteki DLL; wszystkie aplikacje używają tej samej kopii w pamięci, do czasu aż żadna aplikacja jej nie używa i wtedy jest ona usuwana z pamięci. (W systemach 32- i 64-bitowych, współdzielenie pojawia się jedynie wtedy gdy różne programy ładują bibliotekę z tego samego katalogu; kod, ale nie stos, jest współdzielony między procesami dzięki „mapowaniu pamięci”.) Stąd, nawet jeśli wymagana biblioteka znajduje się w katalogu, w którym się znajdować powinna, np. w katalogu systemowym lub katalogu aplikacji, żadna z jej instancji nie będzie użyta, jeśli inna aplikacja będzie uruchomiona wcześniej z niekompatybilną wersją z prywatnego katalogu. Problem ten może się objawić jako błąd aplikacji 16-bitowej, który pojawia się jedynie jeśli aplikacje uruchamiane są w określonej kolejności.

Powody[edytuj | edytuj kod]

Niekompatybilności między bibliotekami DLL są spowodowane przez:

  • ograniczenia pamięci w połączeniu z brakiem separacji przestrzeni adresowej miedzy procesami w 16-bitowej wersji Windows
  • brak wymuszonych standardów wersjonowania, nazywania i umieszczania w systemie plików dla bibliotek DLL
  • brak wymuszonych standardowych metod na instalowanie i usuwanie oprogramowania (system zarządzania pakietami)
  • specyfikacja interfejsu binarnego dla bibliotek DLL pozwala na tworzenie i publikację niekompatybilnych bibliotek o tej samej nazwie i wersji
  • zbyt proste narzędzia do zarządzania, uniemożliwiające identyfikację problemów przez użytkowników i administratorów
  • łamanie zasady wstecznej kompatybilności przez twórców oprogramowania
  • emisja przez Microsoft pozaplanowych i pilnych aktualizacji dla komponentów systemowych
  • brak możliwości „równoległego” uruchamiania we wczesnych wersjach Windows różnych wersji tej samej biblioteki
  • poleganie na bieżącym katalogu lub zmiennej środowiskowej %PATH%, które są zmienne zarówno w czasie jak i zależą od systemu, aby znaleźć wymaganą bibliotekę (zamiast ładować ją z jawnie skonfigurowanej lokalizacji)
  • wykorzystywanie identyfikatorów klas COM z aplikacji przykładowych dla swoich interfejsów i aplikacji, zamiast wygenerowania własnych GUID-ów

Piekło DLL było bardzo powszechnym zjawiskiem w systemach operacyjnych Microsoft przed wydaniem Windows NT, głównym powodem było to, że w 16-bitowych systemach operacyjnych nie ograniczano procesów do ich wyłącznej przestrzeni adresowej, tym samym uniemożliwiono ładowania własnych wersji współdzielonych modułów z którymi były one kompatybilne. Od instalatorów aplikacji oczekiwano uczciwości a tym samym weryfikacji informacji o wersji biblioteki przed nadpisaniem istniejących bibliotek systemowych. Standardowe narzędzia do ułatwiania tego procesu (które zawsze wiązało się z dystrybucją bibliotek zależnych od systemu operacyjnego) były dostarczane przez Microsoft i innych dostawców. Microsoft nawet wymagał od dostawców oprogramowania używania standardowego instalatora i wystawiał certyfikaty o ich prawidłowym działaniu, zanim uprawniał do używania logo Microsoft. Rozwiązanie w postaci uczciwego instalatora nie złagodziło problemu, a wzrost popularności internetu dostarczył więcej możliwości uzyskania aplikacji, które nie spełniały tych wymagań.

Złośliwe oprogramowanie[edytuj | edytuj kod]

Dwuznaczność, które biblioteki nie w pełni kwalifikowane mogą być załadowane w systemie operacyjnym Windows została wykorzystana w ostatnich latach przez złośliwe oprogramowanie, otwierając nową klasę luk, która ma wpływ na aplikacje od wielu dostawców oprogramowania, jak również na sam Windows[3].

Rozwiązania[edytuj | edytuj kod]

Różne formy piekła DLL zostały rozwiązane lub złagodzone przez lata.

Statyczne łączenie[edytuj | edytuj kod]

Najprostszym rozwiązaniem na piekło DLL w aplikacji jest statyczne łączenie ze wszystkimi bibliotekami[4]. Jest to powszechne dla aplikacji w C/C++, gdzie zamiast martwić się o wersję zainstalowanej biblioteki MFC43.DLL, aplikacja jest skompilowana i statycznie połączona z tymi bibliotekami. Pozwala to eliminację wszystkich bibliotek DLL i jest opłacalne dla samodzielnych aplikacji korzystających z bibliotek, które oferują możliwość statycznego łączenia, takich jak MFC. Główny cel bibliotek DLL (współdzielenie kodu uruchomieniowego między programami w celu redukcji zapotrzebowania na pamięć) jest poświęcony za cenę większych programów i braku możliwości korzystania z poprawek zabezpieczeń.

Windows File Protection[edytuj | edytuj kod]

Problem nadpisywania bibliotek DLL został trochę zredukowany dzięki Windows File Protection[5] (ochrona plików Windows), która została wprowadzona w Windows 2000[6]. Uniemożliwia to nieautoryzowanemu oprogramowaniu nadpisanie bibliotek systemowych, chyba, że użyją one odpowiednich metod z Windows API, które na to zezwalają. W dalszym ciągu istnieje ryzyko, że aktualizacje od Microsoft mogą być niekompatybilne z istniejącymi aplikacjami, lecz jest ono zwykle zmniejszone w obecnych wersjach Windows dzięki zastosowaniu side-by-side assembly[7].

Aplikacje firm zewnętrznych nie mogą modyfikować plików systemowych, z wyjątkiem przypadków, gdy zawierają oficjalne komponenty aktualizacji systemu Windows, lub jeśli wyłączą serwis Window File Protection w czasie instalacji, oraz w systemie Windows Vista lub nowszym, przejmą pliki systemowe na własność aby uzyskać do nich dostęp. Takie zmiany można anulować w dowolnym czasie za pomocą narzędzia SFC.

Równoczesne uruchamianie sprzecznych bibliotek[edytuj | edytuj kod]

To rozwiązanie pozwala na korzystanie z różnych kopii tej samej biblioteki w każdej aplikacji, zarówno na dysku jak i w pamięci.

Proste ręczne rozwiązanie na konflikt polega na umieszczeniu różnych kopii problematycznej biblioteki w folderze aplikacji, zamiast we wspólnym folderze systemowym. Rozwiązanie takie jest skuteczne dla aplikacji 32- i 64-bitowych, pod warunkiem, że biblioteka nie korzysta z współdzielenia pamięci. W przypadku aplikacji 16-bitowych, dwie takie aplikacje nie mogą być uruchomione równocześnie na platformie 16-bitowej lub 16-bitowej maszynie wirtualnej w systemie 32-bitowym. Zapobiegało temu OLE przed Windows XP, ponieważ we wcześniejszych wersjach Windows miał jeden rejestr obiektów COM dla wszystkich aplikacji.

Windows XP wprowadził rozwiązanie zwane side-by-side assembly[7], które ładuje oddzielne kopie bibliotek DLL dla każdej aplikacji, które tego wymagają (i tym samym pozwala aplikacjom wymagającym sprzecznych bibliotek na ich równoczesne uruchomienie). Rozwiązanie to eliminuje konflikty pozwalając aplikacjom na ładowanie unikalnych wersji modułów do ich przestrzeni adresowej, z zachowaniem głównego zysku współdzielenia bibliotek między aplikacjami (tj. zmniejszenia użycia pamięci) przez zastosowanie techniki mapowania pamięci to współdzielenia tego samego kodu między różnymi procesami, które korzystają z tych samych modułów. Jednak z takiego podejścia nie mogą korzystać biblioteki współdzielące dane między procesami[8]. Jednym z efektów ubocznych jest to, że osierocone biblioteki DLL mogą nie być aktualizowane w sposób zautomatyzowany.

Aplikacje przenośne[edytuj | edytuj kod]

Aplikacje przenośne są wydajnym rozwiązaniem na zmniejszenie problemu piekła DLL, ponieważ każdy program zawiera swoje wymagane biblioteki w zestawie (zwanym czasami „prywatną biblioteką”[6]). Mechanizm polega na tym, że polityka Windows wobec współdzielonych bibliotek faworyzuje biblioteki dostępne lokalnie (w katalogu aplikacji), a następnie pochodzące z folderów systemowych[9]. Czasami aplikacje są uruchamiane w „bańce”, wykorzystując wirtualizację aplikacji, która unika instalowania plików DLL w systemie operacyjnym.

Przypisy

  1. Avoiding DLL Hell: Introducing Application Metadata in the Microsoft .NET Framework. Microsoft, paździenik 2000.
  2. Steve White, Leslie Muller: Registration-Free Activation of COM Components: A Walkthrough (ang.). Microsoft, lipiec 2005. [dostęp 2012-07-02].
  3. Secure Loading of Libraries to Prevent DLL Preloading Attacks (ang.). Microsoft, 2011-06-11. [dostęp 2012-07-03].
  4. Tim Pfeiffer: Windows DLLs: Threat or Menace? (ang.). Dr. Dobbs Journal, 1998-06-01. [dostęp 2010-07-07]. [zarchiwizowane z tego adresu (2006-06-20)].
  5. Windows File Protection and Windows (ang.). Microsoft, 2001-12-04. [dostęp 2012-07-03].
  6. 6,0 6,1 Rick Anderson: The End of DLL Hell (ang.). 2000-01-11. [dostęp 2012-07-03]. [zarchiwizowane z tego adresu (2001-06-05)].
  7. 7,0 7,1 Side-by-side Assemblies (ang.). 2010-07-08. [dostęp 2012-07-03].
  8. How do I share data in my DLL with an application or with other DLLs? (ang.). [dostęp 2012-07-03].
  9. Arnaud Desitter: Using static and shared libraries across platforms; Rząd 9: Ścieżka do biblioteki (ang.). 2007-06-15. [dostęp 2012-07-03]. [zarchiwizowane z tego adresu (2008-06-01)].

Linki zewnętrzne[edytuj | edytuj kod]