Почему компоновщик С++ требует файлы библиотеки во время сборки, хотя я динамически связываю?

У меня есть исполняемый файл С++, и я динамически связываюсь с несколькими библиотеками (Boost, Xerces-c и custom libs).

Я понимаю, почему мне потребуются файлы .lib/.a, если я захочу статически связать эти библиотеки (соответствующий вопрос SO здесь). Однако зачем мне предоставлять соответствующие файлы библиотеки .lib/.so при связывании моего исполняемого файла, если я динамически связываюсь с этими внешними библиотеками?

Ответы

Ответ 1

Компилятор не знает о динамической компоновке, он просто знает, что существует функция через свой прототип. Линкером нужны файлы lib для разрешения символа. Библиотека для DLL содержит дополнительную информацию, такую ​​как DLL, в которой работают функции и как они экспортируются (по имени, по порядку и т.д.). Файлы lib для DLL содержат гораздо меньше информации, чем файлы lib, содержащие полный объектный код - libcmmt.lib на моей системе - 19,2 МБ, но msvcrt.lib - всего лишь 2,6 МБ.

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

Ответ 2

Раймонд Чен написал пару записей в блогах об этом для Windows. Начните с Классическая модель для компоновки, а затем выполните Почему есть ли у нас библиотеки импорта?.

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

У причины .DLLs нет всей информации, необходимой для связи с ними напрямую, является историческим, а не техническим ограничением.

Ответ 3

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

Ответ 4

У линкера есть задача проверить, что все ваши символы undefined учитываются как со статическим содержимым, так и с динамическим контентом.

По умолчанию, он настаивает на том, чтобы присутствовали все ваши символы.

Однако это только значение по умолчанию. Смотрите -z и --allow-shlib- undefined и друзей.

Ответ 5

Возможно, это динамическое связывание выполняется через библиотеки импорта (перед определением функция имеет __declspec (dllimport)).
Если это так, как компилятор ожидает, что функция __imp_symbol объявлена, и эта функция отвечает за переадресацию вызова в нужную библиотеку, динамически загруженную.
Эти функции генерируются при связывании символов с ключевым словом __declspec (dllimport)

Ответ 6

Вот очень упрощенное описание, которое может помочь. Статическая привязка ставит весь код, необходимый для запуска вашей программы в исполняемый файл, чтобы все было найдено. Динамическое связывание означает, что некоторые из требуемого кода не попадают в исполняемый файл и будут найдены во время выполнения. Где я его найду? Есть ли функция x()? Как сделать вызов функции x()? Это то, что библиотека сообщает компоновщику, когда вы динамически связываете.