Как работает библиотека импорта? Детали?

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

Когда я хочу использовать DLL Win32, обычно я просто вызываю API, например LoadLibrary() и GetProcAdderss(). Но в последнее время я разрабатываю DirectX9, и мне нужно добавить файлы d3d9.lib, d3dx9.lib и т.д.

Я достаточно слышал, что LIB предназначен для статической привязки, а DLL - для динамической компоновки.

Итак, мое нынешнее понимание заключается в том, что LIB содержит реализацию методов и статически связан во время ссылки как часть окончательного EXE файла. Хотя DLL динамически загружается во время выполнения и не является частью окончательного EXE файла.

Но иногда некоторые файлы LIB поставляются с DLL файлами, поэтому:

  • Для чего нужны эти файлы LIB?
  • Как они достигают того, для чего они предназначены?
  • Есть ли какие-либо инструменты, которые позволяют мне проверять внутренности этих файлов LIB?

Обновление 1

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

Обновление 2

Так же, как сказал RBerteig, в файлах LIB, связанных с DLL, есть код-заглушка. Таким образом, вызывающая последовательность должна быть такой:

Мое главное приложение → заглушка в LIB → реальной целевой DLL

Итак, какая информация должна содержаться в этих LIB? Я мог бы подумать о следующем:

  • Файл LIB должен содержать полный путь к соответствующей DLL; Таким образом, DLL может быть загружена средой выполнения.
  • Относительный адрес (или смещение файла?) каждой точки входа метода экспорта DLL должен быть закодирован в заглушке; Таким образом, могут быть сделаны правильные вызовы переходов/методов.

Я прав? Есть что-то еще?

BTW: Есть ли какой-либо инструмент, который может проверять библиотеку импорта? Если я увижу это, больше не будет никаких сомнений.

Ответ 1

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

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

Если он связан неявно при создании программы, то заглушки для каждого экспорта DLL, используемые программой, связаны с программой из библиотеки импорта, и эти заглушки обновляются, когда EXE и DLL загружаются при запуске процесса, (Да, я упростил больше, чем немного здесь...)

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

Смутно, статическая версия той же библиотеки также будет отправлена ​​как .LIB файл. Нет никакого тривиального способа рассказать им обособленно, за исключением того, что LIB, которые являются библиотеками импорта для DLL, обычно будут меньше (часто намного меньше), чем соответствующий статический LIB.

Если вы используете инструментальную цепочку GCC, кстати, вам не нужны библиотеки импорта в соответствии с вашими DLL. Версия компоновщика Gnu, портированного в Windows, напрямую понимает библиотеки DLL и может синтезировать большинство любых необходимых заглушек на лету.

Update

Если вы просто не можете удержаться, зная, где все гайки и болты действительно и что действительно происходит, в MSDN всегда есть что-то, что может помочь. Matt Pietrek article Глубокий взгляд на формат исполняемого файла Win32 Portable - это очень полный обзор формата EXE файл и как он загружается и запускается. Он даже был обновлен, чтобы охватить .NET и многое другое, поскольку он изначально появился в журнале MSDN ca. 2002.

Кроме того, может быть полезно узнать, как узнать, какие библиотеки DLL используются программой. Инструментом для этого является Dependency Walker, aka depend.exe. Версия его включена в Visual Studio, но последняя версия доступна от ее автора по адресу http://www.dependencywalker.com/. Он может идентифицировать все DLL, которые были указаны во время соединения (как ранняя загрузка, так и загрузка с задержкой), а также может запускать программу и следить за загрузкой любых дополнительных DLL файлов во время выполнения.

Обновление 2

Я изложил некоторые из более ранних текстов, чтобы уточнить его при повторном чтении, и использовать термины искусства неявные и явные ссылки для согласованности с MSDN.

Итак, у нас есть три способа, которыми библиотечные функции могут быть доступны для использования программой. Очевидный следующий вопрос заключается в следующем: "Как выбрать, каким образом?"

Статическая связь - это то, как связана основная часть самой программы. Все ваши объектные файлы перечислены и собраны вместе в файл EXE с помощью компоновщика. По пути, компоновщик заботится о небольших задачах, таких как исправление ссылок на глобальные символы, чтобы ваши модули могли вызывать функции друг друга. Библиотеки также могут быть статически связаны между собой. Объектные файлы, составляющие библиотеку, собираются вместе библиотекарем в файле .LIB, который компоновщик ищет модули, содержащие необходимые символы. Один из эффектов статического связывания заключается в том, что к нему привязаны только те модули из библиотеки, которые используются программой; другие модули игнорируются. Например, традиционная математическая библиотека C включает в себя множество функций тригонометрии. Но если вы ссылаетесь на него и используете cos(), вы не получите копию кода для sin() или tan(), если вы также не вызвали эти функции. Для больших библиотек с богатым набором функций это выборочное включение модулей важно. На многих платформах, таких как встроенные системы, общий размер кода, доступного для использования в библиотеке, может быть большим по сравнению с пространством, доступным для хранения исполняемого файла в устройстве. Без выборочного включения было бы сложнее управлять деталями построения программ для этих платформ.

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

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

Итак, первый большой выбор, который нужно сделать при планировании нового проекта, - это динамическая и статическая привязка. Со статической связью у вас меньше файлов для установки, и вы невосприимчивы к третьим сторонам, обновляя используемую вами DLL. Тем не менее, ваша программа больше, и она не так хороша, как гражданин экосистемы Windows. С динамической связью у вас есть больше файлов для установки, возможно, у вас есть проблемы с сторонним обновлением используемой DLL, но вы, как правило, дружелюбны к другим процессам в системе.

Большое преимущество DLL заключается в том, что его можно загружать и использовать без перекомпиляции или даже перезапуска основной программы. Это может позволить стороннему поставщику библиотеки (например, Microsoft и C runtime, например) исправить ошибку в своей библиотеке и распространить ее. После того, как конечный пользователь установит обновленную DLL, они сразу получат выгоду от этого исправления ошибок во всех программах, которые используют эту DLL. (Если это не сломает вещи. См. DLL Hell.)

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

Ответ 2

Существует три типа библиотек: статические, общие и динамически загружаемые библиотеки.

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

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

Ответ 3

Эти файлы библиотеки импорта .LIB используются в следующем свойстве проекта Linker->Input->Additional Dependencies при создании пакета dll, которому требуется дополнительная информация в времени ссылки, которая предоставляется библиотекой импорта .LIB. В приведенном ниже примере, чтобы не получить ошибки компоновщика, мне нужно ссылаться на dll A, B, C и D через их файлы lib. (обратите внимание, что для компоновщика, чтобы найти эти файлы, вам может понадобиться включить их путь развертывания в Linker->General->Additional Library Directories else, вы получите ошибку сборки из-за невозможности найти любой из предоставленных файлов lib.)

Linker- > Input- > Дополнительные зависимости

Если ваше решение создает все динамические библиотеки, возможно, вам удалось избежать этой явной спецификации зависимостей, полагаясь вместо этого на флаги ссылок, открытые в диалоговом окне Common Properties->Framework and References. Кажется, что эти флаги автоматически связывают ваше имя с помощью файлов *.lib. Структура и ссылки

Это, однако, так, как он говорит об общих свойствах, которые не являются конфигурационными или специфичными для платформы. Если вам нужно поддерживать смешанный сценарий сборки, как в нашем приложении, у нас была конфигурация сборки для визуализации статической сборки и специальной конфигурации, которая построила ограниченную сборку подмножества сборок, которые были развернуты как динамические библиотеки. Я использовал флаги Use Library Dependency Inputs и Link Library Dependencies, установленные в разных случаях, для того, чтобы создавать вещи, а затем реализовывать упрощение, но при введении моего кода в статические сборки я представил тонну предупреждений компоновщика, и сборка была невероятно медленный для статических построений. Я запустил введение такого рода предупреждений...

warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)

И я закончил с использованием спецификации руководства Additional Dependencies, чтобы удовлетворить компоновщик для динамических построений, сохраняя при этом статические строители счастливыми, не используя общее свойство, которое их замедлило. Когда я развертываю динамическую сборку подмножества, я только развертываю файлы dll, так как эти файлы lib используются только во время соединения, а не во время выполнения.