Изменение порядка загрузки Windows DLL? (порядок загрузки, а не порядок поиска) - PullRequest
13 голосов
/ 16 июня 2011

Скажите, что у меня есть один исполняемый файл: app.exe

Я использую 2 разные сторонние DLL в этом исполняемом файле: foo.dll bar.dll, и Приложение должно неявно связываться с этими DLL, то есть я не могу использовать ::LoadLibrary для их загрузки.

(Примечание: это не значит, что я не могу вызвать LoadLibrary, но эти библиотеки DLL требуют статического связывания (библиотеки C ++ с __declspec(dllexport)), поэтому вызов LoadLibrary не имеет никакого смысла, потому что исполняемый загрузчик уже вызвал она.)

Эти две DLL не не имеют каких-либо зависимостей друг от друга, то есть их порядок загрузки, насколько я могу судить, не определен (а должен быть неактуальным). (Зависимости обоих типов в основном только от стандартных Windows-библиотек (kernel32, msvcrt и т. Д.)

Теперь у меня проблема с тем, что я хочу контролировать порядок загрузки этих библиотек DLL, то есть мне бы хотелось, чтобы foo.dll был всегда загружен (DLL_PROCESS_ATTACH) до bar.dll.

Можно ли как-то сказать загрузчику Windows DLL загружать одну DLL раньше другой?

Редактировать: Чтобы проверить порядок загрузки DLL исполняемого файла, можно использовать утилиту DUMPBIN.exe: (просто запустите командную строку Visual Studio)

Редактировать: Согласно этому ответу / этой записи в блоге , загрузчик NT последовательно проходит раздел импорта. (Это приведет к загрузке независимых DLL в том порядке, в котором они указаны в разделе импорта.)

C:\path\to\program> dumpbin /IMPORTS app.exe | grep -i \.dll
  MSVCR80D.dll
  KERNEL32.dll
  OLEAUT32.dll
  MSVCP80D.dll
  foo.dll
  bar.DLL

Этот вывод означает, что MSVCR80D.dll (и его зависимости [a] ) будет загружен первым, а bar.DLL будет загружен последним. Выгрузка будет происходить в обратном порядке.

То, что я еще не выяснил, это как повлиять на этот порядок загрузки ...


(Примечания)

[a]: Это, конечно, означает, что, например, Сначала будет загружен kernel32.dll, потому что msvcr80d.dll будет зависеть от kernel32.dll.


Согласно некоторым запросам, я добавляю обоснование для этого: (Но , пожалуйста , я все еще интересуюсь этим вообще. Я знаю как обойти проблему MFC. )

Microsoft MFC DLL в своей отладочной версии имеет встроенную функцию обнаружения утечки памяти. (Насколько я могу судить, это тот же механизм, который используется _CrtSetDbgFlag и соответствующими инструментами.)

DLL-библиотека отладки MFC будет выгружать всю неосвобожденную память при ее выгрузке. Теперь, если в вашем процессе есть вторая DLL, независимая от MFC, и эта вторая DLL освобождает память на DLL_PROCESS_DETACH, механизм отчетов MFC сообщит о ложных утечках памяти, если DLL MFC выгружается раньше других dll.

Если бы можно было убедиться, что отладочная MFC DLL загружается первой / выгружается последней из всех независимых DLL, то все остальные DLL уже очистились бы после себя, и MFC не сообщит о ложных утечках.

Ответы [ 4 ]

6 голосов
/ 21 октября 2013

То, что я еще не выяснил, это как повлиять на этот порядок загрузки ...

Понятия не имею, почему я не пробовал это, но кажется, что импортпорядок секций полученного модуля зависит от порядка, в котором файлы компоновки lib предоставляются компоновщику.

Configuration Properties -> Linker -> Additional Dependencies ...

Файлы lib, перечисленные здесь первыми, также являются первыми в секции импорта, то есть загрузчик будет импортировать их по порядку (по модулю зависимостей).

Итак, чтобы ответить на этот вопрос: Просто предоставьте компоновщику файлы lib в правильном порядке.

Примечание : я пробовал это на VS2005, и это, кажется, работает.Я не знаю, документировано ли это где-то или изменилось ли оно в более новых версиях VC ++.


Обновление: Хотя это работало тогда, сегодня Я нажал нав случае, если порядок загрузки был , а не , на который должен влиять порядок командной строки компоновщика файлов lib.(Все еще) Понятия не имею почему.(Все еще VS2005)

Мне, однако, удалось заставить его работать, добавив проблемные DLL в список DLL с задержкой загрузки (как в ответ Маке ).


4 голосов
/ 16 июня 2011

Просто добавьте foo.dll к таблице импорта bar.dll, загрузчик ОС обработает все остальное.

Вы можете сделать это без исходного кода для bar.dll, не уверенный, если инструмент editbin имеет такую ​​опцию, но это довольно тривиальное редактирование файла PE.

Вместо этого вы можете использовать параметр реестра, который предварительно загружает библиотеки DLL, но я бы не стал этого делать, вы не хотите, чтобы foo.dll загружался в другие процессы, которые не нуждаются в этом.

4 голосов
/ 16 июня 2011

Вот идея: как насчет пометить их как «Delay Loaded dll» в опциях компоновщика app.exe?

Задержка загрузки позволит вам ссылаться «статически» (т. Е. Без LoadLibrary () et.al), но не будет загружать DLL и выполнять связывание до тех пор, пока оно действительно не понадобится.

Если это опция, то (при условии, что вы можете ждать так долго, то есть не иметь доступа к функциям foo / bar dll перед main ()), вы можете в main () получить доступ к функции (просто получить функцию ptr или что-то еще) в foo.dll во-первых, что загрузит его и свяжет все «статически» связанные функции?

(Возможно, LoadLibrary () запускает ту же самую процедуру ссылки, когда это необходимо. Не уверен. Хотя в вашем коде это выглядело бы чище.)

1 голос
/ 16 июня 2011

Если вы не связываете библиотеку импорта (foo.lib & bar.lib), то загрузчик не будет автоматически загружать библиотеки DLL при запуске, и вы можете вызывать LoadLibrary () в любое время.

Кроме того, я написал небольшую удобную библиотеку для инкапсуляции загружаемых DLL на лету без необходимости иметь дело с LoadLibrary / GetProcAddress напрямую.Вы можете прочитать об этом здесь .

...