В вычислительной технике динамический компоновщик — это часть операционной системы , которая загружает и связывает разделяемые библиотеки, необходимые исполняемому файлу , когда он выполняется (во « время выполнения »), путем копирования содержимого библиотек из постоянного хранилища в оперативную память , заполнения таблиц переходов и перемещения указателей . Конкретная операционная система и формат исполняемого файла определяют, как функционирует динамический компоновщик и как он реализован.
Связывание часто называют процессом, который выполняется при компиляции исполняемого файла , в то время как динамический компоновщик — это специальная часть операционной системы, которая загружает внешние общие библиотеки в работающий процесс , а затем динамически связывает эти общие библиотеки с работающим процессом. Этот подход также называется динамическим связыванием или поздним связыванием .
Динамически подключаемая библиотека , или DLL, — это реализация Microsoft концепции разделяемой библиотеки в операционных системах Microsoft Windows и OS/2 . Эти библиотеки обычно имеют расширение файла , (для библиотек, содержащих элементы управления ActiveX ) или (для устаревших системных драйверов ). Форматы файлов для DLL такие же, как и для файлов Windows EXE , то есть Portable Executable (PE) для 32- и 64-разрядных Windows и New Executable (NE) для 16-разрядных Windows. Как и EXE-файлы, DLL могут содержать код , данные и ресурсы в любой комбинации. DLL
OCX
DRV
Файлы данных с тем же форматом файла , что и DLL, но с другими расширениями файлов и, возможно, содержащими только разделы ресурсов, можно назвать ресурсными DLL. Примерами таких DLL являются библиотеки многоязыкового пользовательского интерфейса с расширением MUI
, библиотеки иконок , иногда имеющие расширение ICL
, и файлы шрифтов , имеющие расширения FON
и FOT
. [1]
В большинстве Unix-подобных систем большая часть машинного кода, составляющего динамический компоновщик, на самом деле является внешним исполняемым файлом, который ядро операционной системы загружает и выполняет первым в адресном пространстве процесса, вновь созданном в результате вызова exec
функций posix_spawn
. Во время компоновки путь динамического компоновщика, который должен использоваться, встраивается в исполняемый образ.
При загрузке исполняемого файла ядро операционной системы считывает с него путь динамического компоновщика, а затем пытается загрузить и выполнить этот другой исполняемый двоичный файл; если эта попытка не удалась, например, из-за отсутствия файла с таким путем, попытка выполнить исходный исполняемый файл не удалась. Затем динамический компоновщик загружает исходный исполняемый образ и все динамически связанные библиотеки, от которых он зависит, и запускает исполняемый файл. В результате имя пути динамического компоновщика является частью двоичного интерфейса приложения операционной системы .
В Unix-подобных системах, использующих ELF для исполняемых образов и динамических библиотек, таких как Solaris , 64-битные версии HP-UX , Linux , FreeBSD , NetBSD , OpenBSD и DragonFly BSD , путь динамического компоновщика, который должен использоваться, встраивается во время компоновки в .interp
раздел сегмента исполняемого файла PT_INTERP
. В этих системах динамически загружаемые общие библиотеки можно идентифицировать по суффиксу имени файла .so
(общий объект).
На динамический компоновщик можно влиять, изменяя его поведение во время выполнения программы или компоновки программы, и примеры этого можно увидеть на страницах руководства по компоновщику времени выполнения для различных Unix-подобных систем. [2] [3] [4] [5] [6] Типичным изменением этого поведения является использование переменных средыLD_LIBRARY_PATH
и , которые корректируют процесс компоновки времени выполнения путем поиска общих библиотек в альтернативных местах и принудительной загрузки и компоновки библиотек, которые в противном случае не были бы, соответственно. Примером является zlibc, [7] также известный как , [a] который облегчает прозрачную распаковку при использовании через хак ; следовательно, можно читать предварительно сжатые (сжатые gzip) данные файлов в системах BSD и Linux, как если бы файлы не были сжаты, по сути, позволяя пользователю добавлять прозрачное сжатие к базовой файловой системе, хотя и с некоторыми оговорками. Механизм является гибким, позволяя тривиальную адаптацию того же кода для выполнения дополнительной или альтернативной обработки данных во время чтения файла, до предоставления указанных данных пользовательскому процессу, который их запросил. [8] [9]LD_PRELOAD
uncompress.so
LD_PRELOAD
В операционной системе Apple Darwin и в операционных системах macOS и iOS , созданных на ее основе, путь динамического компоновщика, который должен использоваться, встраивается во время компоновки в одну из команд загрузки Mach-O в исполняемом образе. В этих системах динамически загружаемые общие библиотеки могут быть идентифицированы либо по суффиксу имени файла .dylib
, либо по их размещению внутри пакета для фреймворка.
Динамический компоновщик не только связывает целевой исполняемый файл с общими библиотеками, но и размещает функции машинного кода в определенных адресных точках в памяти, о которых целевой исполняемый файл знает во время компоновки. Когда исполняемый файл хочет взаимодействовать с динамическим компоновщиком, он просто выполняет машинно-специфическую инструкцию вызова или перехода к одной из этих известных адресных точек. Исполняемые файлы на платформах macOS и iOS часто взаимодействуют с динамическим компоновщиком во время выполнения процесса; известно даже, что исполняемый файл может взаимодействовать с динамическим компоновщиком, заставляя его загружать больше библиотек и разрешать больше символов через несколько часов после своего первоначального запуска. Причина, по которой программа macOS или iOS так часто взаимодействует с динамическим компоновщиком, заключается как в API Cocoa и Cocoa Touch от Apple, так и в языке Objective-C , на котором они реализованы (см. их основные статьи для получения дополнительной информации).
Динамический компоновщик можно заставить изменить некоторые из его поведения; однако, в отличие от других операционных систем типа Unix, эти изменения являются подсказками, которые динамический компоновщик может (и иногда игнорирует) игнорировать. Примеры этого можно увидеть на dyld
странице руководства . [10] Типичным изменением этого поведения является использование переменных окружения DYLD_FRAMEWORK_PATH
и DYLD_PRINT_LIBRARIES
. Первая из ранее упомянутых переменных настраивает путь поиска исполняемых файлов для общих библиотек, тогда как последняя отображает имена библиотек по мере их загрузки и компоновки.
Динамический компоновщик macOS от Apple — это проект с открытым исходным кодом, выпущенный как часть Darwin , и его можно найти в проекте Apple с открытым исходным кодом dyld
. [11]
В Unix-подобных операционных системах, использующих XCOFF , таких как AIX , динамически загружаемые общие библиотеки используют суффикс имени файла .a
.
На динамический компоновщик можно влиять, изменяя его поведение во время выполнения программы или компоновки программы. Типичным изменением этого поведения является использование LIBPATH
переменной окружения . Эта переменная регулирует процесс компоновки во время выполнения, выполняя поиск общих библиотек в альтернативных местах и принудительно загружая и компонуя библиотеки, которые в противном случае не были бы, соответственно.
Динамическое связывание из программ на языке ассемблера в IBM OS/360 и его последователях обычно выполняется с помощью макроса LINK, содержащего инструкцию Supervisor Call , которая активирует процедуры операционной системы, что делает модуль библиотеки, который должен быть связан, доступным для программы. Модули библиотеки могут находиться в "STEPLIB" или "JOBLIB", указанных в картах управления и доступных только для определенного выполнения программы, в библиотеке, включенной в LINKLIST в PARMLIB (указывается во время запуска системы), или в "области пакета ссылок", куда загружаются определенные реентерабельные модули во время запуска системы.
В операционной системе Multics все файлы, включая исполняемые, являются сегментами . Вызов процедуры, не являющейся частью текущего сегмента, заставит систему найти указанный сегмент в памяти или на диске и добавить его в адресное пространство запущенного процесса. Динамическое связывание является нормальным методом работы, а статическое связывание (с использованием связующего ) является исключением.
Динамическое связывание обычно медленнее (требует больше циклов ЦП), чем связывание во время компиляции, [12] как и в случае большинства процессов, выполняемых во время выполнения. Однако динамическое связывание часто более эффективно с точки зрения пространства (на диске и в памяти во время выполнения). Когда библиотека связана статически, каждый запущенный процесс связан со своей собственной копией вызываемых библиотечных функций. Поэтому, если библиотека вызывается много раз разными программами, одни и те же функции в этой библиотеке дублируются в нескольких местах в памяти системы. Использование общих динамических библиотек означает, что вместо связывания каждого файла с его собственной копией библиотеки во время компиляции и потенциальной траты пространства памяти, только одна копия библиотеки когда-либо хранится в памяти за раз, освобождая пространство памяти для использования в другом месте. [13] Кроме того, при динамическом связывании библиотека загружается только в том случае, если она фактически используется. [14]