Что такое dll hell
DLL hell (DLL-кошмар, буквально: DLL-ад) — тупиковая ситуация, связанная с управлением динамическими библиотеками DLL в операционной системе Microsoft Windows.
Аналогичная проблема в других ОС носит название Dependency hell.
Сущность проблемы заключается в конфликте версий DLL, призванных поддерживать определённые функции. DLL hell — пример плохой концепции программирования, которая, подобно скрытой мине, приводит к резкому возрастанию трудностей при усложнении и совершенствовании системы.
Содержание
Описание проблемы
По исходному замыслу, DLL должны быть совместимыми от версии к версии и взаимозаменяемыми в обе стороны.
Реализация механизма DLL такова, что несовместимость и невзаимозаменяемость становится скорее правилом, чем исключением, что приводит к большому количеству проблем.
- Отсутствие стандартов на имена, версии и положение DLL в файловой структуре приводит к тому, что несовместимые DLL легко замещают или отключают друг друга
- Отсутствие стандарта на процедуру установки приводит к тому, что установка новых программ приводит к замещению работающих DLL на несовместимые версии
- Отсутствие поддержки DLL со стороны компоновщиков и механизмов защиты приводит к тому, что несовместимые DLL могут иметь одинаковые имя и версию
- Отсутствуют стандартные инструменты идентификации и управления системой DLL пользователями и администраторами
- Использование отдельных DLL для обеспечения связи между задачами приводит к нестабильности сложных приложений
Для избежания конфликтов обычно используют множество избыточных копий DLL для каждого приложения, что сводит на нет исходную идею получения преимущества от DLL как стандартных модулей, хранящихся один раз в памяти и разделяемых многими задачами. Кроме того, при таком опыте после исправления ошибок в DLL или восстановления системы из архива количество различных DLL, носящих одно и то же имя и выполняющих те же функции, возрастает, а автоматическое обновление версии или исправление ошибок становится невозможным.
История проблемы
Эта проблема возникла в ранних версиях Microsoft Windows.
С подобными же проблемами сталкивались ранние версии Mac OS X, но с использованием других технологий. Не избегают подобных проблем дистрибуторы библиотек Open Source.
Поэтому, когда речь идёт о не-майкрософтовской среде, эту ситуацию называют dependency hell (кошмар зависимостей).
Проблема постоянно повторяется, когда программу пытаются запустить не с той DLL, c которой она тестировалась, что показывает изначальную порочность общей концепции, позволяющей произвольную замену версий модулей.
Меры против DLL hell
Данные меры рекомендуют предпринимать одновременно для получения наилучшего результата:
Напишите отзыв о статье "DLL hell"
Ссылки
См. также
- Side-by-side assembly
- Защита файлов Windows
- Защита ресурсов Windows
- Компоновщик
- Динамически подключаемая библиотека
- Global Assembly Cache
Отрывок, характеризующий DLL hell
– Я не во время кажется, – сказал он, – я бы не приехал, но мне дело есть, – сказал он холодно…
– Нет, я только удивляюсь, как ты из полка приехал. – «Dans un moment je suis a vous», [Сию минуту я к твоим услугам,] – обратился он на голос звавшего его.
– Я вижу, что я не во время, – повторил Ростов.
Выражение досады уже исчезло на лице Бориса; видимо обдумав и решив, что ему делать, он с особенным спокойствием взял его за обе руки и повел в соседнюю комнату. Глаза Бориса, спокойно и твердо глядевшие на Ростова, были как будто застланы чем то, как будто какая то заслонка – синие очки общежития – были надеты на них. Так казалось Ростову.
– Ах полно, пожалуйста, можешь ли ты быть не во время, – сказал Борис. – Борис ввел его в комнату, где был накрыт ужин, познакомил с гостями, назвав его и объяснив, что он был не статский, но гусарский офицер, его старый приятель. – Граф Жилинский, le comte N.N., le capitaine S.S., [граф Н.Н., капитан С.С.] – называл он гостей. Ростов нахмуренно глядел на французов, неохотно раскланивался и молчал.
Жилинский, видимо, не радостно принял это новое русское лицо в свой кружок и ничего не сказал Ростову. Борис, казалось, не замечал происшедшего стеснения от нового лица и с тем же приятным спокойствием и застланностью в глазах, с которыми он встретил Ростова, старался оживить разговор. Один из французов обратился с обыкновенной французской учтивостью к упорно молчавшему Ростову и сказал ему, что вероятно для того, чтобы увидать императора, он приехал в Тильзит.
– Нет, у меня есть дело, – коротко ответил Ростов.
Ростов сделался не в духе тотчас же после того, как он заметил неудовольствие на лице Бориса, и, как всегда бывает с людьми, которые не в духе, ему казалось, что все неприязненно смотрят на него и что всем он мешает. И действительно он мешал всем и один оставался вне вновь завязавшегося общего разговора. «И зачем он сидит тут?» говорили взгляды, которые бросали на него гости. Он встал и подошел к Борису.
– Однако я тебя стесняю, – сказал он ему тихо, – пойдем, поговорим о деле, и я уйду.
– Да нет, нисколько, сказал Борис. А ежели ты устал, пойдем в мою комнатку и ложись отдохни.
– И в самом деле…
Они вошли в маленькую комнатку, где спал Борис. Ростов, не садясь, тотчас же с раздраженьем – как будто Борис был в чем нибудь виноват перед ним – начал ему рассказывать дело Денисова, спрашивая, хочет ли и может ли он просить о Денисове через своего генерала у государя и через него передать письмо. Когда они остались вдвоем, Ростов в первый раз убедился, что ему неловко было смотреть в глаза Борису. Борис заложив ногу на ногу и поглаживая левой рукой тонкие пальцы правой руки, слушал Ростова, как слушает генерал доклад подчиненного, то глядя в сторону, то с тою же застланностию во взгляде прямо глядя в глаза Ростову. Ростову всякий раз при этом становилось неловко и он опускал глаза.
– Я слыхал про такого рода дела и знаю, что Государь очень строг в этих случаях. Я думаю, надо бы не доводить до Его Величества. По моему, лучше бы прямо просить корпусного командира… Но вообще я думаю…
– Так ты ничего не хочешь сделать, так и скажи! – закричал почти Ростов, не глядя в глаза Борису.
Борис улыбнулся: – Напротив, я сделаю, что могу, только я думал…
В это время в двери послышался голос Жилинского, звавший Бориса.
– Ну иди, иди, иди… – сказал Ростов и отказавшись от ужина, и оставшись один в маленькой комнатке, он долго ходил в ней взад и вперед, и слушал веселый французский говор из соседней комнаты.
Ростов приехал в Тильзит в день, менее всего удобный для ходатайства за Денисова. Самому ему нельзя было итти к дежурному генералу, так как он был во фраке и без разрешения начальства приехал в Тильзит, а Борис, ежели даже и хотел, не мог сделать этого на другой день после приезда Ростова. В этот день, 27 го июня, были подписаны первые условия мира. Императоры поменялись орденами: Александр получил Почетного легиона, а Наполеон Андрея 1 й степени, и в этот день был назначен обед Преображенскому батальону, который давал ему батальон французской гвардии. Государи должны были присутствовать на этом банкете.
В вычислении , DLL ад термин для осложнений , которые возникают , когда один работает с динамически подключаемых библиотек (DLL) , используемых с Microsoft Windows операционных систем , в частности , унаследованных 16-разрядных версий , которые все работают в одном пространстве памяти.
DLL Hell может проявляться по-разному, при этом приложения не запускаются и не работают правильно.
DLL Hell - это специфическая для экосистемы Windows форма ада общих концепций .
СОДЕРЖАНИЕ
Проблемы
DLL - это реализация разделяемых библиотек Microsoft . Общие библиотеки позволяют объединить общий код в оболочку, DLL, и использовать его любым прикладным программным обеспечением в системе без загрузки нескольких копий в память. Простым примером может служить текстовый редактор графического интерфейса пользователя , который широко используется во многих программах. Поместив этот код в DLL, все приложения в системе могут использовать его без использования дополнительной памяти. Это контрастирует со статическими библиотеками , которые функционально похожи, но копируют код непосредственно в приложение. В этом случае каждое приложение увеличивается на размер всех используемых им библиотек, а для современных программ он может быть довольно большим.
Проблема возникает, когда версия библиотеки DLL на компьютере отличается от версии, которая использовалась при создании программы. Библиотеки DLL не имеют встроенного механизма обратной совместимости, и даже незначительные изменения в DLL могут сделать ее внутреннюю структуру настолько отличной от предыдущих версий, что попытка их использования обычно приводит к сбою приложения. Статические библиотеки позволяют избежать этой проблемы, потому что версия, которая использовалась для сборки приложения, включена в нее, поэтому даже если более новая версия существует в другом месте в системе, это не влияет на приложение.
Основной причиной несовместимости версий является структура файла DLL. Файл содержит каталог отдельных методов (процедур, подпрограмм и т. Д.), Содержащихся в DLL, и типов данных, которые они принимают и возвращают. Даже незначительные изменения в коде DLL могут привести к переупорядочению этого каталога, и в этом случае приложение, которое вызывает определенный метод, полагая, что это 4-й элемент в каталоге, может в конечном итоге вызвать совершенно другую и несовместимую процедуру, которая будет обычно вызывает сбой приложения.
Существует несколько проблем, с которыми обычно сталкиваются библиотеки DLL, особенно после того, как в системе были установлены и удалены многочисленные приложения. Сложности включают конфликты между версиями DLL, трудности с получением необходимых DLL и наличие множества ненужных копий DLL.
Несовместимые версии
DLL топает
Распространенная и неприятная проблема возникает, когда недавно установленная программа перезаписывает рабочую системную DLL более ранней несовместимой версией. Ранние примеры этого были ctl3d.dll и ctl3dv2.dll библиотеки для Windows 3.1 : Microsoft-созданные библиотеки, сторонние издатели распространять с их программным обеспечением, но каждый раздаточный версию они разработали с , а не самой последней версии. Вытеснение DLL происходит по следующим причинам:
- В прошлом Microsoft распределяла библиотеки времени выполнения как общие системные компоненты (первоначально C: \ WINDOWS и C: \ WINDOWS \ SYSTEM) как способ эффективного совместного использования кода в ОС с общей памятью с ограниченным объемом оперативной памяти и дискового пространства. Следовательно, сторонние разработчики также распространяли их таким образом.
- Установщики приложений обычно выполняются в привилегированном контексте безопасности, который имеет доступ для установки библиотек DLL в системные каталоги и для редактирования системного реестра для регистрации новых библиотек DLL как объектов COM . Поэтому плохо написанный или неправильно настроенный установщик может понизить версию системной библиотеки в устаревших версиях Windows, в которых защита файловWindows или защита ресурсов Windows не откатывают изменение. В Windows Vista и более поздних версиях только учетная запись «доверенного установщика» может вносить изменения в основные библиотеки операционной системы.
- Приложениям Windows было разрешено включать обновления ОС в свои собственные программы установки. То есть многие библиотеки DLL Microsoft являются распространяемыми , что означает, что приложения могут включать их, если им нужны службы конкретных библиотек.
- До установщика Windows установщики Windows исторически были коммерческими продуктами; многие люди пытались написать свои собственные установщики, не обращая внимания на проблемы с версией или неправильно решая их в процессе.
- Некоторые среды разработки не добавляли автоматически ресурс версии в свои скомпилированные библиотеки, поэтому многие разработчики упустили этот аспект. Проверка дат файлов, перезапись существующих файлов или пропуск операции копирования, если DLL уже была установлена, были единственными доступными вариантами вместо правильного управления версиями.
- Иногда сама ОС удаляла или заменяла библиотеки DLL более старыми или устаревшими версиями. Например, Windows 2000 установит библиотеки DLL черно-белого принтера поверх DLL с поддержкой цвета, если после цветного принтера был установлен черно-белый принтер.
Неправильная регистрация COM
Общие модули в памяти
16-разрядные версии Windows (и Windows в Windows ) загружают только один экземпляр любой данной DLL; все приложения ссылаются на одну и ту же копию в памяти, пока никакие приложения не используют ее, и она не выгружается из памяти. (Для 32-разрядных и 64-разрядных версий Windows совместное использование между процессами происходит только тогда, когда разные исполняемые файлы загружают модуль из одного и того же каталога; код, но не стек , совместно используется процессами через процесс, называемый «отображение памяти». Таким образом, даже если желаемая DLL находится в каталоге, где можно ожидать, что она будет найдена, например, в системном каталоге или каталоге приложения, ни один из этих экземпляров не будет использоваться, если другое приложение было запущено с несовместимой версией из третий каталог. Эта проблема может проявляться как ошибка 16-разрядного приложения, которая возникает только при запуске приложений в определенном порядке.
Отсутствие работоспособности
В прямом конфликте с проблемой выталкивания DLL: если обновления библиотеки DLL не влияют на все приложения, которые ее используют, то становится намного сложнее "обслуживать" DLL, то есть устранять проблемы, существующие в текущих версиях DLL. . (Исправления безопасности - особенно убедительный и болезненный случай.) Вместо исправления только последней версии DLL разработчик в идеале должен внести свои исправления и протестировать их на совместимость с каждой выпущенной версией DLL.
Причины
Несовместимость DLL была вызвана:
- Ограничения памяти в сочетании с отсутствием разделения пространства памяти процесса в 16-битных версиях Windows;
- Отсутствие принудительных стандартных схем управления версиями, именами и расположением файловой системы для библиотек DLL;
- Отсутствие принудительного стандартного метода установки и удаления программного обеспечения ( управление пакетами );
- Отсутствие централизованной авторитетной поддержки управления двоичным интерфейсом приложения DLL и средств защиты, что позволяет выпускать несовместимые библиотеки DLL с тем же именем файла и внутренними номерами версий;
- Упрощенные инструменты управления, предотвращающие идентификацию измененных или проблемных DLL пользователями и администраторами;
- Разработчики нарушают обратную совместимость функций в разделяемых модулях;
- Microsoft выпускает внеполосные обновления для компонентов среды выполнения операционной системы;
- Неспособность более ранних версий Windows запускать параллельно конфликтующие версии одной и той же библиотеки;
- Использование текущего каталога или %PATH% переменной среды, которые меняются со временем и от системы к системе, для поиска зависимых библиотек DLL (вместо загрузки их из явно настроенного каталога);
- Разработчики повторно используют идентификаторы ClassID из примеров приложений для COM-интерфейсов своих приложений, вместо того, чтобы создавать свои собственные новые идентификаторы GUID .
DLL Hell был очень распространенным явлением в версиях операционных систем Microsoft до Windows NT, основная причина заключалась в том, что 16-разрядные операционные системы не ограничивали процессы своим собственным пространством памяти, тем самым не позволяя им загружать свою собственную версию общий модуль, с которым они были совместимы. Ожидалось, что установщики приложений будут хорошими гражданами и проверят информацию о версии DLL перед перезаписью существующих системных DLL. Стандартные инструменты для упрощения развертывания приложений (которые всегда включают доставку зависимых библиотек DLL операционной системы) были предоставлены Microsoft и другими сторонними поставщиками инструментов. Microsoft даже потребовала, чтобы поставщики приложений использовали стандартный установщик и чтобы их программа установки была сертифицирована для правильной работы, прежде чем им будет разрешено использовать логотип Microsoft. Подход «добросовестного установщика» не смягчил проблему, поскольку рост популярности Интернета предоставил больше возможностей для получения несовместимых приложений.
Использование вредоносным ПО
Неопределенность, с которой не полностью квалифицированные библиотеки DLL могут быть загружены в операционную систему Windows, в последние годы использовалась вредоносными программами , открывая новый класс уязвимостей, который затрагивает приложения от многих различных поставщиков программного обеспечения, а также саму Windows.
Решения
Различные формы DLL-ада были решены или смягчены на протяжении многих лет.
Статическое связывание
Простым решением проблемы DLL Hell в приложении является статическое связывание всех библиотек, то есть включение требуемой версии библиотеки в программу, вместо того, чтобы выбирать системную библиотеку с указанным именем. Это распространено в приложениях C / C ++, где вместо того, чтобы беспокоиться о том, какая версия MFC42.DLL установлена, приложение компилируется для статической компоновки с теми же библиотеками. Это полностью исключает библиотеки DLL и возможно в автономных приложениях, использующих только библиотеки, которые предлагают статический вариант, как это делает библиотека Microsoft Foundation Class . Однако основная цель DLL - совместное использование библиотек времени выполнения между программами для уменьшения накладных расходов памяти - принесена в жертву; дублирование кода библиотеки в нескольких программах приводит к раздуванию программного обеспечения и усложняет развертывание исправлений безопасности или новых версий зависимого программного обеспечения.
Защита файлов Windows
Проблема перезаписи DLL (называемая Microsoft DLL Stomping ) была несколько уменьшена с помощью Windows File Protection (WFP), которая была представлена в Windows 2000 . Это предотвращает перезапись системных библиотек DLL неавторизованными приложениями, если они не используют определенные API Windows, которые это разрешают. По-прежнему может существовать риск того, что обновления от Microsoft несовместимы с существующими приложениями, но этот риск обычно снижается в текущих версиях Windows за счет использования параллельных сборок .
Сторонние приложения не могут обрабатывать файлы ОС, если они не связывают законные обновления Windows со своим установщиком или если они не отключают службу защиты файлов Windows во время установки, а в Windows Vista или более поздних версиях также приобретают права собственности на системные файлы и предоставляют себе доступ. SFC утилита может восстановить эти изменения в любое время.
Одновременный запуск конфликтующих DLL
Решения здесь состоят в наличии разных копий одних и тех же библиотек DLL для каждого приложения как на диске, так и в памяти.
Простым ручным решением конфликтов было размещение различных версий проблемной DLL в папки приложений, а не в общую общесистемную папку. В целом это работает, если приложение является 32-разрядным или 64-разрядным и DLL не использует разделяемую память. В случае 16-разрядных приложений два приложения не могут выполняться одновременно на 16-разрядной платформе или на одной и той же 16-разрядной виртуальной машине в 32-разрядной операционной системе. OLE предотвращало это до Windows 98 SE / 2000, потому что более ранние версии Windows имели единый реестр COM-объектов для всех приложений.
Windows 98 SE / 2000 представила решение, называемое параллельной сборкой , которое загружает отдельные копии DLL для каждого приложения, которому они требуются (и, таким образом, позволяет приложениям, требующим конфликтующих DLL, работать одновременно). Этот подход устраняет конфликты, позволяя приложениям загружать уникальные версии модуля в свое адресное пространство, сохраняя при этом основное преимущество совместного использования библиотек DLL между приложениями (т. Е. Сокращение использования памяти) за счет использования методов сопоставления памяти для совместного использования общего кода между различными процессами, которые все еще работают. используйте тот же модуль. Однако библиотеки DLL, использующие общие данные между несколькими процессами, не могут использовать этот подход. Одним из отрицательных побочных эффектов является то, что потерянные экземпляры DLL могут не обновляться во время автоматизированных процессов.
Портативные приложения
В зависимости от архитектуры приложения и среды выполнения переносимые приложения могут быть эффективным способом уменьшить некоторые проблемы с DLL, поскольку каждая программа связывает свои собственные частные копии любых требуемых DLL. Механизм основан на том, что приложения не полностью определяют пути к зависимым библиотекам DLL при их загрузке, а операционная система выполняет поиск в каталоге исполняемых файлов до любого общего расположения. Однако этот метод также может использоваться вредоносными программами, и повышенная гибкость также может быть достигнута за счет безопасности, если частные библиотеки DLL не обновляются с помощью исправлений безопасности так же, как общие.
Виртуализация приложений также может позволить приложениям работать в «пузыре», что позволяет избежать установки файлов DLL непосредственно в операционную систему.
Другие меры противодействия
Есть и другие меры противодействия DLL Hell, некоторые из которых, возможно, придется использовать одновременно; некоторые другие функции, которые помогают смягчить проблему:
Аналогичная проблема в других ОС носит название Dependency hell.
Содержание
Описание проблемы
По исходному замыслу, DLL должны быть совместимыми от версии к версии и взаимозаменяемыми в обе стороны.
Реализация механизма DLL такова, что несовместимость и невзаимозаменяемость становится скорее правилом, чем исключением, что приводит к большому количеству проблем.
- Отсутствие стандартов на имена, версии и положение DLL в файловой структуре приводит к тому, что несовместимые DLL легко замещают или отключают друг друга
- Отсутствие стандарта на процедуру установки приводит к тому, что установка новых программ приводит к замещению работающих DLL на несовместимые версии
- Отсутствие поддержки DLL со стороны компоновщиков и механизмов защиты приводит к тому, что несовместимые DLL могут иметь одинаковые имя и версию
- Отсутствуют стандартные инструменты идентификации и управления системой DLL пользователями и администраторами
- Использование отдельных DLL для обеспечения связи между задачами приводит к нестабильности сложных приложений
Для избежания конфликтов обычно используют множество избыточных копий DLL для каждого приложения, что сводит на нет исходную идею получения преимущества от DLL как стандартных модулей, хранящихся один раз в памяти и разделяемых многими задачами. Кроме того, при таком опыте после исправления ошибок в DLL или восстановления системы из архива количество различных DLL, носящих одно и то же имя и выполняющих те же функции, возрастает, а автоматическое обновление версии или исправление ошибок становится невозможным.
История проблемы
Эта проблема возникла в ранних версиях Microsoft Windows.
С подобными же проблемами сталкивались ранние версии Mac OS X, но с использованием других технологий. Не избегают подобных проблем дистрибуторы библиотек Open Source.
Поэтому, когда речь идёт о не-майкрософтовской среде, эту ситуацию называют dependency hell (кошмар зависимостей).
Проблема постоянно повторяется, когда программу пытаются запустить не с той DLL, c которой она тестировалась, что показывает изначальную порочность общей концепции, позволяющей произвольную замену версий модулей.
Меры против DLL hell
Данные меры рекомендуют предпринимать одновременно для получения наилучшего результата:
Ссылки
Wikimedia Foundation . 2010 .
Полезное
Смотреть что такое "DLL hell" в других словарях:
DLL Hell — Der Ausdruck DLL Konflikt (auch DLL Hell, deutsch: „DLL Hölle“ genannt) bezeichnet ein Problem, das durch die Installation von Dynamic Link Library (DLLs) auf den Betriebssystemen der Windows Reihe (Windows 3.x, Windows 9x, Windows ME, Windows NT … Deutsch Wikipedia
DLL hell — noun Problems caused by incompatible versions of a DLL (dynamic link library). A more succinct way to say this is that the versioning the CLR enforces for strong named assemblies means the end of DLL hell … Wiktionary
DLL-Hölle — Der Ausdruck DLL Konflikt (auch DLL Hell, deutsch: „DLL Hölle“ genannt) bezeichnet ein Problem, das durch die Installation von Dynamic Link Library (DLLs) auf den Betriebssystemen der Windows Reihe (Windows 3.x, Windows 9x, Windows ME, Windows NT … Deutsch Wikipedia
DLL-Konflikt — Der Ausdruck DLL Konflikt (auch DLL Hell, deutsch: „DLL Hölle“ genannt) bezeichnet ein Problem, das durch die Installation von Dynamic Link Library (DLLs) auf den Betriebssystemen der Microsoft Windows Reihe entstehen kann. Vorwiegend sind ältere … Deutsch Wikipedia
. when altering one's mind becomes as easy as programming a computer, what does it mean to be human.
среда, 11 ноября 2015 г.
Выбираемся из DLL Hell
Это перевод Getting Out of DLL Hell. Автор: Реймонд Чен.
DLL HELL. Проблема настолько коварна, что она имеет своё собственное негативное прозвище. Вы устанавливаете одну программу, и вдруг какая-то другая, казалось бы, не связанная программа перестаёт работать. Это потому, что, хотя вам это неизвестно, но две программы связаны через общий файл DLL. Эти две программы могут иметь различные идеи о том, какую версию файла MSVCRT.DLL нужно хранить в системном каталоге. Или первая программа может обновить элемент управления ActiveX, который также использует вторая программа - сделать обновление, с которым вторая программа не полностью совместима.
Если обе программы имеют важное значение для повседневных операций вашей компании, то для вас, на самом деле, не имеет значения, какая программа виновата. Важно то, что вам нужно заставить работать их обе. Существует не так много различий между "Мы теряем деньги" и "Мы теряем деньги из-за Боба". В любом случае ваша компания теряет деньги - и это ваша работа, решить эту проблему сразу.
Когда-то давно вам приходилось выбирать какая программа "выиграла", а какая - "проиграла". Но сейчас (начиная с Windows 2000) вам доступны способы разрешения таких конфликтов. Эти способы, однако, являются лишь временными решениями, чтобы вернуть вашу систему в рабочее состояние, пока вы/разработчики программ исследуете более постоянное решение конфликта.
В Windows 2000 реализована минимальная версия технологии, поставляемой сейчас под модным названием Dynamic-Link Library Redirection (перенаправление библиотек динамической компоновки). Чтобы включить перенаправление библиотеки DLL, создайте файл с тем же именем, что и файл программы, для библиотек DLL которой требуется перенаправление, но добавив .local к имени файла. Например, чтобы использовать перенаправление для программы C:\Program Files\Litware Inc\Invoice.exe следует создать файл C:\Program Files\Litware Inc\Invoice.exe.local . Содержимое файла не имеет значения; важен сам факт существования этого файла.
Осталось только заметить что .local файлы и папки игнорируются, если приложение использует манифесты для управления зависимостями.
Перенаправление библиотек DLL не позволит полностью избежать кошмара библиотек DLL, но, по крайней мере, оно предоставляет средства первой помощи для контроля над ситуацией на время решения проблемы.
В данной статье речь пойдёт о высокоуровневом взгляде на компоновку. Где ищутся разделяемые библиотеки на Linux, BSD*, Mac OS X, Windows, от которых зависят приложения? Что делать с обратной совместимостью? Как бороться с адом зависимостей?
Предполагается, что читатель знаком с такими наборами символов как «компилятор», «объектный файл», «компоновщик», «статическая библиотека», «динамическая библиотека», «динамический загрузчик» и некоторыми другими, поэтому разжёвывать мы ничего не будем.
Проблемы статической загрузки динамических библиотек:
- main.exe зависит от version-0.3.dll и bar.dll. bar в свою очередь, зависит от version-0.2.dll, которая бинарно не совместима с версией 0.3 (не просто символы отсутствуют, а совпадают имена, но различное число аргументов, или создают объекты разной природы и т. п.). Затрут ли символы из version-0.2.dll оные из version-0.3.dll? Тот же вопрос стоит тогда, когда используется одна статическая версия библиотеки (скажем, version-0.2.lib) и динамическая (version-0.3.dll);
- создание перемещаемых приложений: где динамический загрузчик будет искать version-0. dll и bar.dll для приложения из предыдущего пункта? Найдёт ли он зависимости main.exe, если тот будет перемещён в другую папку? Как нужно собрать main.exe, чтобы зависимости искались относительно исполняемого файла?
- dependency hell: две версии одной библиотеки /opt/kde3/lib/libkdecore.so и /opt/kde4/lib/libkdecore.so (с которой плазма уже не падает), половина программ требуют первую, другая половина программ — вторую. Обе библиотеки нельзя поместить в одну область видимости (один каталог). Эта же проблема есть и в п. 1, т. к. надо поместить две версии библиотеки version в один каталог.
Другим примером в рамках огромного проекта служит тот факт, что выпуск различных частей имеет разный период (с чем наша команда столкнулась и что вообще послужило причиной написания данного текста). Таким образом, главный продукт может время от времени выходить в конфигурации, описанной в пункте 1.
Dependency hell больше актуален для разработчиков системных библиотек и операционных систем, но и в прикладной области может возникнуть. Опять же предположим, что имеется огромный проект, в котором несколько исполняемых программ. Все они зависят от одной библиотеки, но разных версий. (Это не та же ситуация, что и в п. 1: там в один процесс загружается две версии одной библиотеки, а здесь в каждый процесс загружается только одна, но каждый использует свою версию).
Побег из ада
Ответ прост: надо добавить версию в имя файла библиотеки. Это позволит размещать файлы библиотек в одном каталоге. При этом рекомендуется добавлять версию ABI, а не API, порождая тем самым две параллельных ветки версий и соответствующие трудности.
- x – мажорный выпуск. Ни о какой совместимости речи не идёт;
- y – минорный выпуск. Либа совместима на уровне исходных текстов, но двоичная совместимость может быть сломана;
- z – багофикс. Либа совместима в обе стороны.
Будут работать и приложения, использующие version-1.1.0, и те, кто использует version-1.0.x.
Если совместимость сломали, то в системе будет два файла и снова всё будет работать.
Если по каким-то причинам совместимость сломали при багофиксе, то должна быть увеличена минорная версия (и нечего фейлится, как это сделала команда любимейшего Qt [1] ).
Кстати говоря, никто не запрещает вообще включить версию API – тогда символических ссылок будет больше, т.к. совместимость чаще сохраняется. Зато в этом случае упомянутый фейл Qt разрулился бы легко и не заставил увеличивать минорную версию.
Это справедливо для всех платформ.
Решение оставшихся двух вопросов отличается в зависимости от ОС.
ELF & GNU ld (Linux, *BSD, etc)
В разделяемой библиотеке формата ELF присутствует так называемое SONAME [2] [3] . Это – строка символов, которая прописывается в двоичный файл в секцию DT_SONAME. Просмотреть SONAME для библиотеки можно, например, так:
Если программа/библиотека faz связывается с библиотекой baz, которая имеет SONAME = baz-0.dll, то строка baz-0.dll будет жёстко прописана в двоичном файле faz в секции DT_NEEDED, и при его запуске динамический загрузчик будет искать файл с именем baz-0.dll. При этом никто не запрещает назвать файл по-другому!
- список каталогов в секции DT_RPATH, которая жёстко прописана в исполняемом файле. Поддерживается большинством *nix-систем. Игнорируется, если присутствует секция DT_RUNPATH;
- LD_LIBRARY_PATH – переменная окружения, также содержит список каталогов;
- DT_RUNPATH – тоже самое, что и DT_RPATH, только просматривается после LD_LIBRARY_PATH. Поддерживается только на самых свежих Unix-подобных системах;
- /etc/ld.so.conf – файл настроек динамического загрузчика ld.so, который содержит список папок с библиотеками;
- жёстко зашитые пути – обычно /lib и /usr/lib.
На этапе компоновки библиотек version-0.x задаются SONAME:
Они зависят только от системных библиотек и поэтому не требуют наличия секций R[UN]PATH.
Библиотека bar уже зависит от version-0.2, поэтому нужно указать RPATH:
Параметр --enable-new-dtags указывает компоновщику заполнить секцию DT_RUNPATH.
Параметр -Wl,-rpath. позволяет заполнить секцию R[UN]PATH. Для задания списка путей можно указать параметр несколько раз, либо перечислить все пути через двоеточие:
Теперь всё содержимое папки result целиком или саму папку можно перемещать по файловой системе как угодно, но при запуске динамический загрузчик найдёт все зависимости и программа исполнится:
Вот мы и подошли к проблеме затирания символов! Bar использует version-0.2.dll, в которой get_number() возвращает 2, а само приложение version-0.3.dll, где та же функция возращает уже 3. По выводу приложения видно, что одна версия функции get_number затирается другой.
Дело в том [6; Dynamic Linking and Loading, Comparison of dynamic linking approaches] , что GNU ld & ELF не использует SONAME или имя файла в качестве пространства имён для импортируемых символов:
если разные библиотеки экспортируют сущности с одними и теми же именами, то одни из них будут перетирать другие и в лучшем случае программа упадёт.
Случай, когда одна из библиотек суть статическая, решается просто: все символы статической библиотеки должны быть скрыты [7, 2.2.2 Define Global Visibility] .
К сожалению, в случае динамических библиотек не всё так просто. У компоновщика/загрузчика GNU отсутствует такая функциональность, как прямое связывание [8] . Кто-то пилил эту возможность в Генту [9] , но кажется, всё заглохло. В Солярке она есть [10] [11] , но сама Солярка сдохла…
Одним из возможных вариантов является версионирование самих символов [7, 2.2.5 Use Export Maps] . На самом деле это больше похоже на декорирование символов. (Можно только представлять, что сейчас кричит читатель, программирующий на Си++. )
Данный способ заключается в том, чтобы создать так называемый версионный сценарий, в котором перечислить все экспортируемые и скрытые сущности [12] [13] . Пример сценария из version-0.3:
На этапе компоновки указать данный файл с помощью параметра --version-script=/path/to/version.script . После этого приложение, которое будет связано с такой либой получит в NEEDED version-0.3.dll, а в таблице импорта неопределённый символ get_number@@VERSION_0.3 , хотя в заголовочных файлах по-прежнему будет просто int get_number().
Натравите nm на любую программу, которая использует glibc, и вы прозреете!
- Эй, Дарт! Наша libX будет поддерживать Линукс!
- Noooooooooooooooooooooooooo!
Да, после фейла, с которым наша команда столкнулась, капитан принял волевое решение и теперь используется только одна версия либы (именно из-за Линукса).
Как обстоят дела на Маке?
Мак Ось использует формат Mach-o для исполняемых файлов, а для поиска символов двух-уровневое пространство имён [14, Two-level namespace] [16] . Это по-умолчанию сейчас, но можно собрать с плоским пространством имён или вообще отключить его при запуске программы [15, FORCE_FLAT_NAMESPACE] . Узнать, собран ли бинарник с поддержкой пространства имён поможет команда:
То есть не надо париться с каким-то дополнительным декорированием имён – просто включить версию в имя файла!
А что же с поиском зависимостей?
В макоси почти всё аналогично, только называется по-другому.
Вместо SONAME есть id библиотеки или install name. Просмотреть можно, например, так: Изменить можно с помощью install_name_tool.
При связывании с библиотекой её id прописывается в бинарнике.
Просмотреть зависимости бинарника можно так:или
При запуске dyld пытается открыть файл с именем «id» [15, DYNAMIC LIBRARY LOADING] , т. е. рассматривает install name как абсолютный путь к зависимости. Если потерпел неудачу – то ищет файл с именем/суффиксом «id» в каталогах, перечисленных в переменной окружения DYLD_LIBRARY_PATH (полный аналог LD_LIBRARY_PATH).
Если поиск в DYLD_LIBRARY_PATH не дал результатов, то dyld аналогично просматривает ещё парочку переменных окружения [15] , после чего поищет либу в стандартных каталогах.
Такая схема не позволяет собирать перемещаемые приложения, поэтому была введена специальная метка, которую можно прописывать в id: @executable_path/. Эта метка во время загрузки будет развёрнута в абсолютный путь до исполняемого файла.
- main.bin
- plugin/
- 1.plugin
- helper.dylib
Для решения этой проблемы яблочники с версии Оси 10.4 ввели новый маркер: @loader_path/. Во время загрузки зависимости, этот маркер развернётся в абсолютный путь к бинарнику, который дёргает зависимость.
Последняя сложность заключается в том, что надо две версии связываемых библиотек: одни будут устанавлены в систему, и иметь а другие использованы для сборки проектов, и их Это легко решить с помощью install_name_tool, но утомительно; поэтому с версии 10.5 ввели метку @rpath/. Библиотека собирается с и копируется куда угодно. Бинарник собирается со списком путей для поиска зависимостей, в котором разрешено использовать @_path/:
Это аналогично RPATH/RUNPATH для ELF. При запуске бинарника строка @rpath/libfoo.dylib будет развёрнута в @executable_path/libs/libfoo.dylib, которая уже развернётся в абсолютный путь. Либо развернётся в /usr/lib/libfoo.dylib.Просмотреть зашитые в бинарник rpath'ы можно так:
Удалить, изменить или добавить rpath'ы можно с помощью install_name_tool.Проверяем на примере:
На айОС всё так же.Как видно из примера, Mac OS X в плане динамических библиотек лучше Linux & Co.
И наконец, Windows!
Тут тоже всё хорошо [6; Dynamic Linking and Loading, Comparison of dynamic linking approaches] . Надо только добавить версию в имя файла и… симлинков нет! То есть они есть, но на них многие жалуются и работают они только на NTFS
(Windows XP точно можно установить на FAT раздел). Следовательно, обратная совместимость может стоить приличного места на диске… Ну и ладно. )Чтобы собрать пример на Windows потребуется запустить консоль Visual Studio, в которой уже будет настроено окружение. Далее сборка и запуск:
Либы ищутся только так [17] . Одним из возможных способов смены алгоритма поиска зависимостей является использование файла настроек приложения (application configuration file) и свойства privatePath у probing [18] . Однако данный способ применим только начиная с Windows 7/Server 2008 R2.А ещё есть WinSxS и так называемые сборки (assemblies) [19] . Это – тема отдельной статьи. Однако пока писалась эта статья, снизошло озарение и понимание, что эти самые сборки нужны лишь для того (по крайней мере, Сишникам и Си++никам) чтобы все приложения компоновались, скажем, с comdlg32.dll, но все использовали разные версии.
Заключение
Все основные платформы позволяют относительно просто создавать приложения, которые могут быть установлены обычным копированием. Однако проблемы dependency hell, обратной совместимости и затирания символов разработчики должны решать самостоятельно.
Основным решением является выбор правильного версионирования и контроля за ним.
Пользуясь возможностью, хочу поблагодарить своих коллег Александра Сидорова и Александра Прокофьева за конструктивную критику и ценные замечания!
Читайте также: