Динамическое связывание - Linux Vs. Windows


10

Под Windows, когда я компилирую код C / C ++ в проекте DLL в MSVC, я получаю 2 файла:

  1. MyDll.dll
  2. MyDll.lib

где, насколько я понимаю, MyDll.libсодержит какую-то таблицу указателей с указанием расположения функций в dll. При использовании этой dll, скажем, в exe-файле, MyDll.libон внедряется в exe-файл во время компоновки, поэтому во время выполнения он «знает», где находятся функции, MyDll.dllи может их использовать.

Но если я скомпилирую тот же код в Linux, я получу только один файл MySo.soбез MySo.a(эквивалент libфайла в Linux), так как же исполняемый файл в Linux знает, где находятся функции, MySo.soесли во время компоновки ничего в него не встроено?

Ответы:


1

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

MySo.aв случае создания фактически включал бы символы, которые должны быть связаны непосредственно в двоичный файл, вместо «таблиц поиска символов», используемых в Windows.

ответ rustyx объясняет процесс в Windows более подробно, чем я могу; я давно не пользовался Windows.


1
«Windows использует другой подход ... указать операционной системе, где именно символы находятся в DLL» - это противоречит вики , в котором говорится, что имена функций по-прежнему разрешаются (при запуске или при первом вызове функции библиотеки), даже когда вы используйте порядковые номера (если не используется прямая привязка адреса, чего никто не сделал бы, потому что это заставляет пользователей библиотеки перекомпилировать и повторно развертывать свой код при каждом изменении библиотеки).
Югр

@yugr Убрал эту часть, я все равно хватался за соломинку.
SS Anne

4

Компоновщик MSVC может связывать вместе объектные файлы (.obj) и объектные библиотеки (.lib) для создания .EXE или .DLL.

Чтобы связать с DLL, процесс в MSVC должен использовать так называемую библиотеку импорта (.LIB), которая действует как связующее звено между именами функций C и таблицей экспорта DLL (в DLL функция может быть экспортирована по имени или по порядковому номеру - последний часто использовался для недокументированных API).

Однако в большинстве случаев таблица экспорта DLL имеет все имена функций, и поэтому библиотека импорта (.LIB) содержит в значительной степени избыточную информацию (« функция импорта ABC -> экспортированная функция ABC » и т. Д.).
Можно даже сгенерировать .LIB из существующего .DLL.

Компоновщики на других платформах не имеют этой «функции» и могут напрямую связываться с динамическими библиотеками.


«Линкеры на других платформах не имеют этой функции», хотя их легко реализовать (например, Implib.so делает это для Linux), чтобы добиться отложенной загрузки и других полезностей.
югр

@yugr: именно поэтому «функция» заключена в кавычки - это не то, что вы обычно хотите делать, а дополнительная работа, которую вы должны выполнять в Windows.
Крис Додд

1

Разница, которую вы видите, заключается в большей степени детализации реализации - под Linux и Windows работают одинаково - вы вызываете функцию-заглушку, которая статически связана в вашем исполняемом файле, и эта заглушка затем загружает DLL / shlib, если необходимо (в случае задержки загрузка , иначе библиотека загружается при запуске программы) и (при первом вызове) разрешает символ через GetProcAddress/dlsym .

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

Эти два подхода настолько похожи, что фактически можно имитировать библиотеки импорта Windows в Linux (см. Проект Implib.so ).


0

В Linux вы переходите MySo.soк компоновщику, и он может извлечь только то, что необходимо для фазы компоновки, добавив ссылку, MySo.soнеобходимую во время выполнения.


-3

.dllили .soявляются общими библиотеками (связаны во время выполнения), а .aи .libявляются статической библиотекой (связаны во время компиляции). В этом нет разницы между Windows и Linux.

Разница в том, как они обрабатываются. Примечание: разница только в таможне, как они используются. Было бы не сложно создать Linux на Windows и наоборот, за исключением того, что практически никто этого не делает.

Если мы используем dll или вызываем функцию даже из нашего собственного двоичного файла, существует простой и понятный способ. Например, в C мы видим, что:

int example(int x) {
  ...do_something...
}

int ret = example(42);

Однако на уровне asm может быть много различий. Например, на x86, callкод операции выполняется, а 42в стеке. Или в некоторых регистрах. Или где угодно. Никто не знает что до написания dll , как он будет использоваться. Или как проекты захотят использовать это, возможно, написанное с помощью компилятора (или на языке!), Которого сейчас даже не существует (или это неизвестно разработчикам библиотеки dll).

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

Как вы видите правильно, Windows обычай состоит в том, что мы создаем dll, мы также создаем минимальный .a/ .libс ним. Эта минимальная статическая библиотека является только оберткой, символы (функции) этой библиотеки доступны через нее. Это делает необходимые преобразования вызовов уровня asm.

Его преимуществом является совместимость. Его недостатком является то, что если у вас есть только .dll, вам может быть трудно понять, как его функции нужно вызывать. Это делает использование dll.a хакерской задачей, если разработчик dll не дает вам . Таким образом, он служит, главным образом, в целях закрытости, например, так что легче получить дополнительные деньги для SDK.

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

В Linux бинарный интерфейс dll является стандартным и соответствует соглашению C. Таким образом, нет .aнеобходимости, и существует совместимость двоичных файлов между общими библиотеками, в обмен на что мы не имеем преимуществ обычая Microsoft.


1
Пожалуйста, предоставьте доказательство того, что функции-заглушки могут изменять порядок аргументов. Я никогда не слышал об этом раньше, и в это трудно поверить, учитывая, насколько велики потери производительности.
Югр

@yugr Простое переупорядочение регистров / стеков не влияет на производительность. Если вы используете DLL-скомпилированные dll-файлы из msvc-скомпилированных двоичных файлов, то, очевидно, произойдет не так уж и много, но это возможно.
Петер - Восстановить Монику

1
Мы могли бы поспорить об этом, но в случае, если вы правы, должно быть легко предоставить корректные ссылки, чтобы функции-заглушки могли выполнять нетривиальную обработку аргументов (и быть не просто фиктивными батутами).
Югр

@yugr У заглушек есть доступ к сигнатурам функций dll, что делает нетривиальную обработку тривиальной.
Петер - Восстановить Монику

1
Я только предлагаю вам дополнить свой ответ несколькими контрольными ссылками о том, что делает библиотека импорта (потому что некоторые претензии сомнительны).
Югр
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.