Существует несколько различных способов загрузки библиотек Windows, но каждый из них служит другой цели.
Использование LoadLibrary()
и GetProcAddress()
предназначено для того, чтобы позволить решениям времени выполнения использовать несколько различных ситуаций динамической компоновки. Эти вызовы - это просто системные вызовы, которые не затрагивают какой-либо аспект результирующего файла PE, а адреса, возвращаемые GetProcAddress()
, не являются особыми в отношении генерации кода. Это означает, что вызывающий объект отвечает за правильное использование этих символов на самом синтаксическом уровне (например, используя правильное соглашение о вызове и правильное количество, размер и выравнивание аргументов, если они есть, для вызова функции).
Ссылка на файл .lib
, связанный с кодом .dll
, отличается. Клиентский код относится к содержимому среды выполнения с использованием внешних идентификаторов, как если бы они были статически связанными символами. Во время процесса сборки компоновщик будет разрешать эти идентификаторы с символами, найденными в файле .lib
. Хотя в принципе эти символы могут указывать на что-либо вообще (как и любой другой символ), автоматически сгенерированный файл .lib
просто предоставит символы, которые действуют как крошечные прокси на содержимое памяти, которое будет заполнено во время загрузки загрузчиком PE.
Способ, которым эти прокси реализованы, зависит от ожидаемого типа доступа к памяти. Например, символ, который ссылается на функцию в .dll
, будет реализован как одна косвенная инструкция jmp
, таким образом, чтобы исходная команда call
из кода клиента попала в команду jmp
из содержимого .lib
и будет перенаправлена на адрес, разрешенный загрузчиком PE во время загрузки. По этому адресу находится код динамически загруженной функции.
Много больше деталей в сторону, все это используется, чтобы инструктировать PE загрузчика делать ту же работу, что и клиентский код будет делать сам по себе: вызов LoadLibrary()
для отображения PE в память и прорыть экспорт таблиц с GetProcAddress()
, чтобы найти и рассчитать правильный адрес для каждого символа. Загрузчик PE выполняет это автоматически во время загрузки из информации, найденной в таблице импорта исполняемого файла клиента.
Других слов, время загрузки динамическое связывание происходит медленнее и толще, чем время выполнения динамического связывания с помощью крошечной суммы, если таковые имеется, но она предназначена для обеспечения более простого интерфейса программирования для обычного разработчика , особенно потому, что многие библиотеки всегда необходимы и всегда доступны. Попытка предоставить какую-либо дополнительную логику при ручной загрузке не пригодится в этих обстоятельствах.
Подводя итоги, не беспокойтесь, чтобы использовать LoadLibrary()
и GetProcAddress()
только потому, что они, похоже, обеспечивают большую производительность или надежность. Ссылка на файлы .lib
по возможности.
Идет далее по этой теме, возможно даже создать изображения PE, которые не содержат любых таблиц импорта () и могут получать доступ к системным вызовам и другим экспортированным программам. Этот подход используется вредоносным ПО, чтобы скрыть подозрительное использование API (например, вызовы Winsock) путем удаления любой информации о времени загрузки.
Единственная причина использования 'GetProcAddress' заключается в том, что вам нужно принять решение о загрузке библиотеки (например, если у вас есть несколько вариантов, плагинов или если библиотека не существует). В противном случае вы должны использовать '.lib' и позволить системе следить за загрузкой и связыванием библиотеки. – nneonneo
@Saustin, что заставляет вас сказать, что библиотека импорта вызовет ненужное раздувание? Все это - заглушка, чтобы помочь компоновщику исправить символы экспорта в DLL. И компоновщик не будет тянуть ненужные символы, если ни один из объектных файлов не ссылается на них. – greatwolf
Итак, вы загружаете функции Windows API? Почему бы просто не связать их с .lib, как это делает каждый другой человек? Подписи функций уже находятся в 'Windows.h'! – nneonneo