Вызов LoadLibrary из DllMain - PullRequest
       16

Вызов LoadLibrary из DllMain

1 голос
/ 06 декабря 2010

MSDN говорит:

Он не должен вызывать функцию LoadLibrary или LoadLibraryEx. (или функция, которая вызывает эти функции), потому что это может создать петли зависимостей в порядке загрузки DLL. Это может привести к использованию DLL до того, как система выполнит свой код инициализации.

Я пытался позвонить LoadLibrary из DllMain, и ничего не произошло.

Единственная проблема, которую я вижу, заключается в том, что загруженная DLL будет использовать функции в моей DLL до выполнения остальной части моего DllMain.

Почему я не должен вызывать LoadLibrary в DllMain?

EDIT:

Хорошо, я понял, что я не должен вызывать LoadLibrary в DllMain только потому, что я должен верить MSDN, как это делают другие верующие (я видел там некоторые неправильные вещи, но я тоже должен их забыть).
И потому что что-то может случиться в более новых версиях Windows (хотя там ничего не изменилось за последние десять лет).

Но может ли кто-нибудь показать код, который будет воспроизводить что-то плохое, что происходит при вызове LoadLibrary в DllMain? В любой существующей ОС Windows?
Не просто вызов одной функции инициализации синглтона внутри другой, а LoadLibrary в DllMain?

Ответы [ 5 ]

12 голосов
/ 07 декабря 2010

Ваш аргумент в пользу продолжения этого, похоже, перефразирует:

Microsoft говорит, не делай этого, но мой один тестовый пример, кажется, работает, поэтому я не понимаю, почему никто не должен делать это.

Вы работаете с большим предположением: вы предполагаете, что базовая реализация загрузчика Windows никогда не изменится. Что, если загрузчик изменяется в «Windows 8» таким образом, что ваш код больше не работает должным образом? Теперь Microsoft обвиняют в этом, и они должны включить еще один хак для совместимости, чтобы обойти код, который они сказали вам , а не , чтобы написать в первую очередь.

Следуйте инструкциям. Они предназначены не только для того, чтобы сделать вашу жизнь более сложной, но и для того, чтобы гарантировать, что ваш код будет работать так же хорошо в Windows будущего, как и сейчас.

12 голосов
/ 10 декабря 2010

Существуют простые и даже не очень простые обстоятельства, при которых вызов LoadLibrary из DllMain совершенно безопасен.Но дизайн заключается в том, что DllMain доверяют не изменять список загруженных модулей.

Хотя наличие блокировки загрузчика действительно ограничивает то, что можно сделать в DllMain, это только косвенно относится к правилу LoadLibrary.Соответствующим назначением блокировки загрузчика является последовательный доступ к списку загруженных модулей.Хотя NTDLL работает с этим списком в одном потоке, владение блокировкой загрузчика гарантирует, что список не будет изменен кодом NTDLL, который выполняется в другом потоке.Однако блокировка загрузчика является критическим разделом.Это не делает ничего, чтобы остановить тот же поток от повторного получения блокировки загрузчика и изменения списка.

Это не имело бы значения, если бы NTDLL держался полностью при работе со списком.Однако NTDLL предусматривает вовлечение в эту работу другого кода, как при инициализации вновь загруженной DLL.Каждый раз, когда NTDLL вызывает вне себя во время работы со списком, есть выбор для дизайна.В общем, есть два варианта.Одним из них является стабилизация списка и снятие блокировки загрузчика, вызов снаружи, затем получение блокировки загрузчика и возобновление работы со списком, как с нуля, потому что внешний вызов мог изменить его.Другой - сохранить блокировку загрузчика и доверять вызываемому коду, чтобы он не делал ничего, что изменяет список.И, таким образом, LoadLibrary становится недоступным в DllMain.

Дело не в том, что блокировка загрузчика делает что-либо для остановки DllMain от вызова LoadLibrary или даже в том, что сама блокировка загрузчика делает такой вызов небезопасным.Вместо этого NTDLL доверяет DllMain удерживать блокировку загрузчика, чтобы не вызывать LoadLibrary.

Для контраста рассмотрим правило DllMain о том, что не нужно ожидать объекты синхронизации.Здесь блокировка загрузчика играет непосредственную роль в том, чтобы сделать это небезопасным.Ожидание объекта синхронизации в DllMain устанавливает возможность взаимоблокировки.Все, что нужно, - это то, что другой поток уже содержит ожидаемый объект, а затем этот другой поток вызывает любую функцию, которая будет ожидать блокировки загрузчика (например, LoadLibrary, но также и такие функции, как, казалось бы, безобидный GetModuleHandle).

Желание растянуть или нарушить правила DllMain может быть вредным или даже глупым.Тем не менее, я должен отметить, что Microsoft, по крайней мере, частично виновата в том, что люди спрашивают, насколько сильны или значимы эти правила.В конце концов, некоторые из них не всегда были задокументированы четко и убедительно, и когда я смотрел в последний раз, они все еще не были задокументированы во всех ситуациях, где они, безусловно, необходимы.(Исключение, которое я имею в виду, заключается в том, что по крайней мере до Visual Studio 2005 программистам MFC, пишущим библиотеки DLL, было приказано поместить свой код инициализации в CWinApp :: InitInstance, но не сказали, что этот код подчиняется правилам DllMain.)

Более того, для кого-то из Microsoft было бы немного богато говорить так, как будто правила DllMain должны соблюдаться без вопросов.Существуют примеры, когда собственные программисты Microsoft нарушают правила, и продолжают это делать даже после того, как считается, что нарушение правил привело к серьезным реальным проблемам.

8 голосов
/ 06 декабря 2010

Как указано в http://msdn.microsoft.com/en-us/library/ms682583%28VS.85%29.aspx:

Потоки в DllMain удерживают блокировку загрузчика, поэтому никакие дополнительные DLL не могут быть динамически загружены или инициализированы.

Приветствия

3 голосов
/ 25 мая 2015

Я работал над делом, которое могло потребовать использования LoadLibrary в DllMain, поэтому при расследовании нашел это обсуждение.Обновление об этом из моего сегодняшнего опыта

Чтение этого может стать действительно страшным http://blogs.msdn.com/b/oleglv/archive/2003/10/28/56142.aspx.Имеют значение не только различные блокировки, но и порядок передачи библиотек компоновщику.Случай, скажем, один би

Теперь я попробовал это с vc9 под win7.Да, так и есть.В зависимости от порядка передачи библиотек компоновщику, работает LoadLibrary или нет.Тем не менее, то же самое с vc11 под win8 работает должным образом, независимо от порядка ссылок.Верификатор приложений в этом не виноват.

Я не призываю использовать его таким образом прямо сейчас и везде :) Но просто к сведению, если то же самое с win10 и далее - это может иметь больше пользы.В любом случае кажется, что механизм загрузки под win8 претерпел некоторые заметные изменения.

Спасибо.

0 голосов
/ 17 апреля 2019

Очень поздно, но все же,

Если в потоке 1 (T1) вы DllMain загружаете другие библиотеки, будет вызываться DllMain этих других библиотек; что само по себе нормально, но говорят, что их DLLMain создает поток (T2) и ожидает события для завершения T2.

Теперь, если T2 загружает библиотеку при ее обработке, загрузчик не сможет получить блокировку, поскольку T1 уже получил ее. Поскольку T2 подвешен на LoaderLock, он никогда не будет сигнализировать о том, что событие T1 ожидает.

Что приведет к тупику.

Такой сценарий мог бы быть более вероятным, я полагаю, что широкая причина в том, что мы не можем быть уверены в том, какой код будет выполняться в других библиотеках, поэтому не стоит этого делать. 1009 *

...