C/С++ Как работает динамическое связывание на разных платформах?
Как работает динамическая компоновка?
В Windows (LoadLibrary) вам нужна DLL для вызова во время выполнения, но во время соединения вам необходимо предоставить соответствующий .lib файл или программа не будет связывать... Что делает файл .lib? Описание методов .dll? Разве это не то, что содержат заголовки?
Кроме того, на * nix вам не нужен файл lib... Как, как компилятор знает, что методы, описанные в заголовке, будут доступны во время выполнения?
Как новичок, когда вы думаете об одной из двух схем, то другой, ни один из них не имеет смысла...
Ответы
Ответ 1
Чтобы ответить на ваши вопросы один за другим:
-
Динамическое связывание отменяет часть процесса связывания во время выполнения.
Его можно использовать двумя способами: неявно и явно.
Неявно статический компоновщик будет вставлять информацию в
исполняемый файл, который заставит библиотеку загружать и разрешать
необходимые символы. Явно, вы должны позвонить LoadLibrary
или
dlopen
вручную, а затем GetProcAddress
/dlsym
для каждого
символ, который вам нужно использовать. Неявная загрузка используется для вещей
как системная библиотека, где реализация будет зависеть от
версия системы, но интерфейс гарантирован.
Явная загрузка используется для таких вещей, как плагины, где
библиотека, которая будет загружена, будет определена во время выполнения.
-
Файл .lib
необходим только для неявной загрузки. Это
содержит информацию, которую библиотека фактически предоставляет
символ, поэтому компоновщик не будет жаловаться на то, что символ
undefined, и он сообщает компоновщику в какой библиотеке символы
, поэтому он может вставить необходимую информацию, чтобы вызвать
эта библиотека будет автоматически загружена. Все файлы заголовков
скажите компилятору, что символы будут где-то существовать;
линкеру нужно .lib
знать, где.
-
В Unix вся информация извлекается из
.so
. Почему Windows требует два отдельных файла, а не
я не знаю, помещая всю информацию в один файл; его
фактически дублируя большую часть информации, поскольку
информация, необходимая в .lib
, также необходима в .dll
.
(Возможно, вопросы лицензирования. Вы можете распространять свою программу с помощью
.dll
, но никто не может ссылаться на библиотеки, если только
они имеют .lib
.)
Главное, чтобы сохранить, что если вы хотите неявной загрузки,
вы должны предоставить компоновщику соответствующую информацию,
либо с файлом .lib
или .so
, чтобы он мог вставить этот
информацию в исполняемый файл. И это, если вы хотите явно
загрузка, вы не можете ссылаться на любой из символов в библиотеке
непосредственно; вам нужно позвонить GetProcAddress
/dlsym
, чтобы получить их
обращается к вам (и делает некоторые забавные кастинга, чтобы использовать их).
Ответ 2
Файл .lib
для Windows не требуется для загрузки динамической библиотеки, он просто предлагает удобный способ сделать это.
В принципе вы можете использовать LoadLibrary
для загрузки DLL, а затем использовать GetProcAddress
для доступа к функциям, предоставляемым этой DLL. Компиляция прилагающейся программы не нуждается в доступе к DLL в этом случае, она нужна только во время выполнения (т.е. Когда LoadLibrary
фактически выполняется). MSDN имеет пример кода.
Недостатком здесь является то, что вам нужно вручную написать код для загрузки функций из dll. В случае, если вы сами скомпилировали dll самостоятельно, этот код просто дублирует знания, которые компилятор мог извлечь из исходного кода dll автоматически (например, имена и подписи экспортируемых функций).
Это то, что делает файл .lib
: он содержит вызовы GetProcAddress
для экспортированных Dlls функций, сгенерированных компилятором, поэтому вам не нужно беспокоиться об этом. В терминах Windows это называется Динамическое связывание времени загрузки, так как Dll загружается автоматически кодом из файла .lib при загрузке прилагаемой программы (в отличие от к ручному подходу, называемому динамической связью во время выполнения).
Ответ 3
Как работает динамическая компоновка?
Файл библиотеки динамической компоновки (aka shared object) содержит инструкции и данные машинного кода вместе с таблицей метаданных, в которой указано, какие смещения в этом коде/данных относятся к каким "символам", типу символа (например, функция vs данные), количество байтов или слов в данных и несколько других вещей. У разных ОС, как правило, есть разные форматы файлов общих объектов, и, действительно, одна и та же ОС может поддерживать несколько, но ее суть.
Итак, представьте разделяемую библиотеку большой кусок байтов с индексом, подобным этому:
SYMBOL ADDRESS TYPE SIZE
my_function 1000 function 2893
my_number 4800 variable 4
В общем, точный тип символов не нужно записывать в таблицу метаданных - он ожидал, что объявления в файлах заголовков библиотеки содержат всю недостающую информацию. С++ является немного особенным - по сравнению с C - потому что перегрузка может означать, что есть несколько функций с одним и тем же именем, а пространства имен допускают дополнительные символы, которые иначе были бы двусмысленно названы, - по этой причине, как правило, используется для конкатенации некоторого представления имена пространств имен и функций для имени функции, образуя что-то, что может быть уникальным в объекте библиотеки.
Программа, которая хочет использовать общий объект, обычно может выполнять одну из двух задач:
-
операционная система загружает как сам, так и совместно используемый объект (до выполнения main()
), а загрузчик ОС отвечает за поиск символов и проверку метаданных в изображении файла программы об использовании этих символы, а затем исправление в символьных адресах в памяти, которую использует программа, так что программа может просто запускаться и работать функционально, как если бы она знала о символьных адресах, когда она была сначала скомпилирована (но, возможно, немного медленнее)
-
или, явно в своем собственном обращении к исходному коду dlopen
через некоторое время после main
, используйте dlsym
или аналогично, чтобы получить адреса символа, сохранить их в (функции/данные) указатели на основе программист знает ожидаемые типы данных, а затем вызывает их явно с помощью указателей.
В Windows (LoadLibrary) вам нужна DLL для вызова во время выполнения, но во время соединения вам необходимо предоставить соответствующий .lib файл или программа не будет связывать...
Это звучит не так. Должен быть тот или другой, о котором я думаю.
Wtf содержит ли файл .lib? Описание методов .dll? Разве это не то, что содержат заголовки?
Файл lib - на этом уровне описания - почти такой же, как файл общих объектов... Главное отличие заключается в том, что компилятор находит адреса символов перед отправкой и запуском программы.
Ответ 4
Кроме того, в OS X (и я предполагаю * nix... dlopen) вам не нужен файл lib... Как, как компилятор знает, что методы, описанные в заголовке, будут доступны во время выполнения?
Компиляторы или линкеры не нуждаются в такой информации. Вы, программист, должны справиться с ситуацией, которая может быть недоступна для разделяемых библиотек, которые вы пытаетесь открыть с помощью dlopen()
.
Ответ 5
Вы можете использовать DLL файл в Windows двумя способами: либо вы связываетесь с ним, и все готово, больше ничего не нужно делать. Или вы загружаете его динамически во время выполнения.
Если вы связываетесь с ним, используется файл библиотеки DLL. Библиотека ссылок содержит информацию, которую линкер использует, чтобы реально узнать, какую DLL загружать и где в DLL-функциях, поэтому он может их вызывать. Когда ваша программа загружена, операционная система также загружает DLL для вас, в основном, что она вызывает LoadLibrary
для вас.
В других операционных системах (например, OS X и Linux) он работает аналогичным образом. Разница в том, что в этих системах компоновщик может напрямую смотреть на динамическую библиотеку (файл .so
/.dynlib
) и выяснить, что нужно без отдельной статической библиотеки, например, в Windows.
Чтобы динамически загружать библиотеку, вам не нужно связываться со всем, что связано с библиотекой, которую вы хотите загрузить.
Ответ 6
Современные системы nix производят процесс динамического связывания с ОС Solaris. Linux, в частности, не нуждается в отдельном файле .lib, потому что все внешние зависимости содержатся в формате ELF. .interp
раздел файла ELF указывает, что в этом исполняемом файле есть внешние символы, которые необходимо было динамически разрешить. Это происходит для динамической компоновки.
Существует способ обработки динамической компоновки в пользовательском пространстве. Этот метод называется динамической нагрузкой. Это когда вы используете системные вызовы для получения указателей на методы из внешних *.so.
Более подробную информацию можно найти в этой статье http://www.ibm.com/developerworks/library/l-dynamic-libraries/.
Ответ 7
Как уже говорили другие: то, что включено в файл .lib
на Windows, включено непосредственно в .so
/.dynlib
в Linux/OS Х. Но главный вопрос: почему?
Не лучше ли решение nix?
Я думаю, что это так, но .lib
имеет одно преимущество. Разработчик, связанный с DLL, фактически не должен иметь доступ к самому DLL файлу.
Часто ли происходит такой сценарий в реальном мире? Стоит ли стараться поддерживать два файла на DLL файл? Я не знаю.
Edit: Хорошо, ребята позволили сделать вещи еще более запутанными! Вы можете напрямую связать DLL с Windows, используя MinGW. Поэтому вся проблема библиотеки импорта не является напрямую, связанной с самой Windows. Взято из sampleDLL статья из MinGW wiki:
Библиотека импорта, созданная с помощью опции компоновщика "-out-implib", требуется iff (== если и только если), то DLL должна быть сопряжена с некоторыми Компилятор C/С++, отличный от инструментальной комбинации MinGW. Инструментальная цепочка MinGW совершенно счастлив напрямую связать с созданной DLL. Подробнее можно найти в файлах информации ld.exe, которые являются частью binutils пакет (который является частью инструментальной цепочки).
Ответ 8
Linux также требует ссылки, но вместо этого для библиотеки .Lib ей необходимо связать динамический компоновщик /lib/ld-linux.so.2
, но это обычно происходит за кулисами при использовании GCC (однако, если вы используете ассемблер, вам нужно указать его вручную).
Оба подхода, как подход Windows.LIB, так и подход к динамическому компоновке динамического компоновщика Linux, рассматриваются в действительности как статическая связь. Однако есть разница в том, что в Windows часть работы выполняется во время ссылки, хотя она все еще работает во время загрузки (я не уверен, но я думаю, что файл .LIB - это просто для компоновщика, который знает физическое имя библиотеки, однако символы разрешаются только во время загрузки), а в Linux все, кроме ссылки на динамический компоновщик, происходит во время загрузки.
Динамическое связывание в основном относится к открытию вручную файла DLL во время выполнения (например, с использованием LoadLinrary()), и в этом случае бремя полностью зависит от программиста.
Ответ 9
В общей библиотеке, например .dll
.dylib
и .so
, есть некоторая информация о имени и адресе символа, например:
------------------------------------
| symbol name | symbol address |
|----------------------------------|
| Foo | 0x12341234 |
| Bar | 0xabcdabcd |
------------------------------------
И функция загрузки, такая как LoadLibrary
и dlopen
, загружает общую библиотеку и делает ее доступной для использования.
GetProcAddress
и dlsym
найдите адрес символа. Например:
HMODULE shared_lib = LoadLibrary("asdf.dll");
void *symbol = GetProcAddress("Foo");
// symbol is 0x12341234
В окнах есть файл .lib
для использования .dll
. Когда вы ссылаетесь на этот .lib
файл, вам не нужно вызывать LoadLibrary
и GetProcAddress
, а просто использовать функцию общей библиотеки, как если бы они были "нормальными" функциями. Как это работает?
Фактически, .lib
содержит информацию об импорте. Это так:
void *Foo; // please put the address of Foo there
void *Bar; // please put the address of Bar there
Когда операционная система загружает вашу программу (строго говоря, ваш модуль), операционная система выполняет автоматически LoadLibrary
и GetProcAddress
.
И если вы пишете такой код, как Foo();
, компилятор автоматически преобразует его в (*Foo)();
. Таким образом, вы можете использовать их, как если бы они были "нормальными" функциями.