Подтвердить что ты не робот

C/С++ Как работает динамическое связывание на разных платформах?

Как работает динамическая компоновка?

В Windows (LoadLibrary) вам нужна DLL для вызова во время выполнения, но во время соединения вам необходимо предоставить соответствующий .lib файл или программа не будет связывать... Что делает файл .lib? Описание методов .dll? Разве это не то, что содержат заголовки?

Кроме того, на * nix вам не нужен файл lib... Как, как компилятор знает, что методы, описанные в заголовке, будут доступны во время выполнения?

Как новичок, когда вы думаете об одной из двух схем, то другой, ни один из них не имеет смысла...

4b9b3361

Ответ 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)();. Таким образом, вы можете использовать их, как если бы они были "нормальными" функциями.