Зачем нам нужен .lib файл в случае импорта функций из .dll? - PullRequest
1 голос
/ 11 мая 2019

Можете ли вы помочь мне понять, зачем нам нужны файлы .lib при импорте функций и данных из dll?

Я слышал, что он содержит список экспортируемых функций и элементов данных изсоответствующий dll, но когда я использовал CFF Explorer для изучения моей dll, я обнаружил, что dll уже имеет адреса экспортирующих функций, поэтому я теоретически могу связать свою программу с .dll без каких-либо дополнительных файлов.

Можете ли вы, пожалуйста,объясните, какие данные хранятся в файлах .lib, более подробно.
И также, да, я знаю, что Visual Studio заставляет нас добавлять файлы .lib в раздел дополнительных зависимостей, но зачем они действительно нужны?

Ответы [ 2 ]

5 голосов
/ 11 мая 2019

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

Когда компоновщик объединяет сгенерированные компилятором объектные файлы, чтобы сделать конечный исполняемый файл, он должен выяснить, на что фактически ссылаются все сгенерированные компилятором ссылки. Если он не может сопоставить данную ссылку с каким-либо фрагментом кода в вашем исполняемом файле, ему нужно вместо этого сопоставить его с внешней DLL. Поэтому необходимо знать, на какие библиотеки даже смотреть, и как эти библиотеки экспортируют вещи. DLL может экспортировать данную функцию / переменную по имени ИЛИ по порядковому номеру, поэтому компоновщику нужен способ сопоставить идентификаторы, используемые ссылками вашего кода, с конкретными записями в таблицах EXPORTS конкретных файлов .dll (особенно в случай, когда вещи экспортируются по порядковым номерам). Файлы Static-link .lib предоставляют компоновщику эту информацию о сопоставлении (т. Е. FunctionA сопоставляется с Ordinal 123 в DLL XYZ.dll, FunctionB сопоставляется с именем _FunctionB@4 в DLL ABC.dll и т. Д.).

Затем компоновщик может заполнить таблицу IMPORTS вашего исполняемого файла информацией о соответствующих необходимых EXPORTS записях, а затем заставить ссылки DLL в вашем коде указывать на правильные IMPORTS записи (если компоновщик может ' Чтобы разрешить сгенерированную компилятором ссылку на фрагмент кода в вашем исполняемом файле или на определенный экспорт DLL, он прерывается с «неразрешенной внешней» ошибкой).

Затем, когда ваш исполняемый файл загружается во время выполнения, загрузчик ОС просматривает таблицу IMPORTS, чтобы узнать, какие экспорты DLL необходимы, поэтому он может затем загрузить соответствующие DLL в память и обновить записи в * Таблица 1024 * с реальными адресами памяти, основанными на таблице EXPORTS каждой библиотеки DLL (если не удается загрузить указанную DLL-библиотеку или не удается найти ссылочный экспорт, загрузчик ОС прерывает загрузку исполняемого файла). Таким образом, когда ваш код вызывает функции DLL или обращается к переменным DLL, эти обращения идут в правильные места.

Все изменится, если ваш исходный код динамически обращается к функциям / переменным DLL через явные вызовы GetProcAddress() во время выполнения. В этом случае файлы static-link .lib не требуются для такого доступа, поскольку ваш собственный код обрабатывает загрузку DLL в память и находит экспорт, который он хочет использовать.

Тем не менее, существует третий вариант, который объединяет вышеупомянутые сценарии: вы можете написать свой код для доступа к функциям / переменным DLL статически , но использовать функцию компоновщика delay-load (если он есть). В этом случае вам по-прежнему нужны файлы static-link .lib для каждой загруженной с задержкой DLL, к которой вы обращаетесь, но компоновщик заполняет отдельную таблицу DELAYLOAD в вашем исполняемом файле ссылками на экспорт DLL вместо заполнения IMPORTS Таблица. Он указывает сгенерированные компилятором ссылки DLL на заглушки в RTL вашего компилятора, которые заменят ссылки на адреса из GetProcAddress() при первом обращении к заглушкам во время выполнения, что исключает необходимость заполнения ссылок загрузчик ОС во время загрузки. Это позволяет вашему исполняемому файлу нормально работать, даже если экспорт DLL отсутствует во время загрузки, и, возможно, даже не нужно загружать библиотеки DLL вообще, если они никогда не используются (конечно, если ваш исполняемый файл пытается получить доступ к экспорту DLL) динамически, и он не загружается, ваш код, скорее всего, потерпит крах, но это отдельная проблема).

1 голос
/ 11 мая 2019

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

В качестве тривиального примера того, почему это не всегда может работать, рассмотрим исполняемый файл, который обращается к двум DLL, одна для фильтра Winsockа другой для распределителя.И скажем, что на этой конкретной машине DLL фильтра Winsock также реализует распределитель с тем же API, а DLL распределителя также реализует фильтр Winsock с тем же API.Как мог компилятор знать, какие функции API доступны из какой DLL?Файл библиотеки содержит намерение при доступе к DLL, то есть API и функции, к которым вы хотите получить доступ.

Важно, что не существует такой вещи, как "Соответствующая DLL".В разных системах могут быть разные файлы DLL.Что нужно знать компоновщику, так это то, как должна выглядеть библиотека DLL, на которую он может положиться, а не то, какой может оказаться библиотека, которую вы можете использовать в какой-то конкретной системе.

Например, предположим,DLL-файл содержит распределитель.У вас может быть один файл DLL для распределителя с отладкой, один для распределителя с оптимизацией под конкретные версии ЦП и один для распределителя, использующего новый экспериментальный алгоритм.Компонент должен знать API-интерфейс, который реализуют все эти DLL-файлы, а не конкретную реализацию в каком-либо одном файле.

Вы можете создать LIB-файл из DLL-файла , но вы можетеЗавершить сборку исполняемого файла, который не работает при использовании какой-либо другой версии файла DLL.Вы должны были бы предположить, что все, что делает эта конкретная DLL, именно то, что будет делать любая другая DLL, которая реализует тот же API.

...