В вычислительной технике DLL Hell — это термин, обозначающий сложности, возникающие при работе с динамически подключаемыми библиотеками (DLL), используемыми в операционных системах Microsoft Windows , [1] особенно в устаревших 16-разрядных версиях , которые все работают в одном пространстве памяти.
DLL Hell может проявляться по-разному, при этом приложения не запускаются и не работают корректно.
DLL Hell — это специфичная для экосистемы Windows форма общей концепции ада зависимостей .
DLL — это реализация общих библиотек Microsoft . Общие библиотеки позволяют объединять общий код в оболочку, DLL, которая используется любым прикладным программным обеспечением в системе без загрузки нескольких копий в память. Простым примером может быть текстовый редактор с графическим интерфейсом , который широко используется во многих программах. Поместив этот код в DLL, все приложения в системе смогут использовать его, не используя больше памяти. Это контрастирует со статическими библиотеками , которые функционально схожи, но копируют код непосредственно в приложение. В этом случае каждое приложение увеличивается на размер всех используемых им библиотек, а для современных программ он может быть весьма большим.
Проблема возникает, когда версия DLL на компьютере отличается от версии, которая использовалась при создании программы. DLL не имеют встроенного механизма обратной совместимости, и даже незначительные изменения в DLL могут сделать ее внутреннюю структуру настолько отличной от предыдущих версий, что попытка их использования обычно приводит к сбою приложения. Статические библиотеки позволяют избежать этой проблемы, поскольку версия, которая использовалась для сборки приложения, включена в нее, поэтому даже если более новая версия существует где-то в системе, это не влияет на приложение.
Основной причиной несовместимости версий является структура файла DLL. Файл содержит каталог отдельных методов (процедур, подпрограмм и т. д.), содержащихся в DLL, и типов данных, которые они принимают и возвращают. Даже незначительные изменения в коде DLL могут привести к перестановке этого каталога, и в этом случае приложение, вызывающее определенный метод, полагая, что это четвертый элемент в каталоге, может в конечном итоге вызвать совершенно другую и несовместимую процедуру, которая будет обычно приводит к сбою приложения.
С DLL обычно возникает несколько проблем, особенно после того, как в системе было установлено и удалено множество приложений. К трудностям относятся конфликты между версиями DLL, трудности с получением необходимых DLL и наличие большого количества ненужных копий DLL.
Решения этих проблем были известны еще в то время, когда Microsoft писала систему DLL . Они были включены в замену .NET «Сборки».
Определенная версия библиотеки может быть совместима с некоторыми программами, использующими ее, и несовместима с другими. Windows особенно уязвима к этому из-за акцента на динамическом связывании библиотек C++ и объектов связывания и внедрения объектов (OLE). Классы C++ экспортируют множество методов, и одно изменение в классе, например новый виртуальный метод, может сделать его несовместимым с программами, созданными на основе более ранней версии. У связывания и внедрения объектов есть очень строгие правила, предотвращающие это: интерфейсы должны быть стабильными, а менеджеры памяти не являются общими. Однако этого недостаточно, поскольку семантика класса может измениться. Исправление ошибки в одном приложении может привести к удалению функции из другого. До Windows 2000 Windows была уязвима для этого, поскольку таблица классов COM была общей для всех пользователей и процессов. Только один COM-объект в одной DLL/EXE может быть объявлен как имеющий определенный глобальный идентификатор COM-класса в системе. Если какой-либо программе требовалось создать экземпляр этого класса, она получала текущую централизованно зарегистрированную реализацию. В результате установка программы, устанавливающей новую версию общего объекта, может непреднамеренно привести к поломке других программ, которые были установлены ранее.
Распространенная и неприятная проблема возникает, когда недавно установленная программа перезаписывает рабочую системную DLL более ранней несовместимой версией. Ранними примерами этого были библиотеки ctl3d.dll
и ctl3dv2.dll
для Windows 3.1 : библиотеки, созданные Microsoft, которые сторонние издатели распространяли вместе со своим программным обеспечением, но каждая из них распространяла версию, которую они разработали, а не самую последнюю версию. [2] Удаление DLL происходит по следующим причинам:
В COM и других частях Windows, до появления параллельных сборок без реестра, [ 5] реестр использовался для определения того, какую базовую DLL использовать. Если была зарегистрирована другая версия модуля, то эта DLL будет загружена вместо ожидаемой. Этот сценарий может быть вызван конфликтом установок, в которых регистрируются разные версии одних и тех же библиотек, и в этом случае преимущественную силу будет иметь последняя установка.
16-разрядные версии Windows (и Windows в Windows ) загружают только один экземпляр любой библиотеки DLL; все приложения ссылаются на одну и ту же копию в памяти до тех пор, пока ни одно приложение не будет использовать ее и она не будет выгружена из памяти. (Для 32-битных и 64-битных версий Windows совместное использование процессов происходит только тогда, когда разные исполняемые файлы загружают модуль из одного и того же каталога; код, но не стек, совместно используется между процессами посредством процесса, называемого «отображение памяти». ) Таким образом, даже если желаемая DLL находится в каталоге, где ее можно ожидать, например, в системном каталоге или каталоге приложения, ни один из этих экземпляров не будет использоваться, если другое приложение запустилось с несовместимой версией из третий каталог. Эта проблема может проявляться в виде 16-битной ошибки приложения, которая возникает только тогда, когда приложения запускаются в определенном порядке.
В прямом противоречии с проблемой топота DLL: если обновления DLL не влияют на все приложения, которые ее используют, то становится намного сложнее «обслуживать» DLL, то есть устранять проблемы, существующие в текущих версиях DLL. . (Исправления безопасности — особенно сложный и болезненный случай.) Вместо исправления только последней версии DLL, в идеале разработчик должен внести свои исправления и проверить их на совместимость с каждой выпущенной версией DLL.
Несовместимость DLL была вызвана:
%PATH%
переменной среды, которые меняются со временем и от системы к системе, для поиска зависимых DLL (вместо загрузки их из явно настроенного каталога);DLL Hell был очень распространенным явлением в версиях операционных систем Microsoft до Windows NT. Основная причина заключалась в том, что 16-битные операционные системы не ограничивали процессы своим собственным пространством памяти, тем самым не позволяя им загружать свою собственную версию общий модуль, с которым они были совместимы. Ожидалось, что установщики приложений будут добросовестными гражданами и проверят информацию о версии DLL, прежде чем перезаписывать существующие системные DLL. Стандартные инструменты для упрощения развертывания приложений (которое всегда включает доставку зависимых библиотек DLL операционной системы) были предоставлены Microsoft и другими сторонними поставщиками инструментов. Microsoft даже требовала от поставщиков приложений использовать стандартный установщик и проверять правильность работы своих программ установки, прежде чем им будет разрешено использование логотипа Microsoft. Подход добросовестного установщика не решил проблему, поскольку рост популярности Интернета предоставил больше возможностей для получения несоответствующих приложений.
Неопределенность, с которой неполные библиотеки DLL могут загружаться в операционную систему Windows, в последние годы использовалась вредоносными программами [ когда? ] , открывая новый класс уязвимостей, которые затрагивают приложения многих поставщиков программного обеспечения, а также саму Windows. [6]
За прошедшие годы были решены или смягчены различные формы ада DLL.
Простое решение DLL-ада в приложении — статически слинковать все библиотеки, т.е. включить в программу нужную версию библиотеки, а не подхватывать системную библиотеку с заданным именем. [7] Это часто встречается в приложениях C/C++, где вместо того, чтобы беспокоиться о том, какая версия MFC42.DLL
установлена, приложение компилируется для статической компоновки с теми же библиотеками. Это полностью исключает использование DLL и возможно в автономных приложениях с использованием только библиотек, которые предлагают статическую опцию, как это делает библиотека классов Microsoft Foundation . Однако основная цель DLL – совместное использование библиотек времени выполнения между программами для уменьшения накладных расходов на память – приносится в жертву; дублирование библиотечного кода в нескольких программах приводит к раздуванию программного обеспечения и усложняет развертывание исправлений безопасности или новых версий зависимого программного обеспечения.
Проблема перезаписи DLL (называемая Microsoft «Stomping DLL» ) была несколько уменьшена с помощью Windows File Protection (WFP), [8] , которая была представлена в Windows 2000 . [9] Это предотвращает перезапись системных DLL неавторизованными приложениями, если только они не используют специальные API-интерфейсы Windows , которые разрешают это. По-прежнему может существовать риск того, что обновления от Microsoft будут несовместимы с существующими приложениями, но в текущих версиях Windows этот риск обычно снижается за счет использования параллельных сборок .
Сторонние приложения не могут вмешиваться в файлы ОС, если они не включают законные обновления Windows в свой установщик или если они не отключают службу защиты файлов Windows во время установки, а в Windows Vista или более поздней версии также не берут на себя ответственность за системные файлы и не предоставляют себе доступ. Утилита SFC может отменить эти изменения в любое время.
Решения здесь состоят в том, чтобы иметь разные копии одних и тех же DLL для каждого приложения как на диске, так и в памяти.
Простое ручное решение конфликтов заключалось в размещении разных версий проблемной DLL в папках приложений, а не в общей общесистемной папке. Обычно это работает, если приложение является 32-битным или 64-битным и DLL не использует общую память. В случае 16-битных приложений два приложения не могут выполняться одновременно на 16-битной платформе или на одной и той же 16-битной виртуальной машине в 32-битной операционной системе. OLE препятствовал этому до Windows 98 SE/2000, поскольку более ранние версии Windows имели единый реестр COM-объектов для всех приложений.
В Windows 98 SE/2000 появилось решение, называемое параллельной сборкой [10] , которое загружает отдельные копии DLL для каждого приложения, которому они требуются (и, таким образом, позволяет приложениям, которым требуются конфликтующие DLL, запускаться одновременно). Этот подход устраняет конфликты, позволяя приложениям загружать уникальные версии модуля в свое адресное пространство, сохраняя при этом основное преимущество совместного использования DLL между приложениями (т. е. сокращение использования памяти) за счет использования методов сопоставления памяти для совместного использования общего кода между различными процессами, которые все еще выполняют операции. используйте тот же модуль. Однако библиотеки DLL, использующие общие данные между несколькими процессами, не могут использовать этот подход. [11] Одним из негативных побочных эффектов является то, что потерянные экземпляры DLL не могут обновляться во время автоматизированных процессов.
В зависимости от архитектуры приложения и среды выполнения переносимые приложения могут быть эффективным способом решения некоторых проблем с DLL, поскольку каждая программа объединяет свои собственные частные копии любых необходимых ей DLL. [9] Этот механизм основан на том, что приложения не полностью определяют пути к зависимым DLL при их загрузке, а операционная система ищет каталог исполняемого файла перед любым общим местоположением. [12] Однако этот метод также может быть использован вредоносным ПО, [13] и повышенная гибкость может также достигаться за счет безопасности, если частные библиотеки DLL не обновляются с помощью исправлений безопасности так же, как и общие библиотеки. .
Виртуализация приложений также может позволить приложениям запускаться в «пузыре», что позволяет избежать установки файлов DLL непосредственно в операционную систему.
Существуют и другие меры противодействия DLL-аду, некоторые из которых, возможно, придется использовать одновременно; некоторые другие функции, которые помогают смягчить проблему: