Введение

Часть операционной системы компьютера

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

Microsoft Windows

Библиотека динамической связи (DLL) — это реализация Microsoft концепции разделяемой библиотеки в операционных системах Microsoft Windows и OS/2. Эти библиотеки обычно имеют расширение файла DLL, OCX (для библиотек, содержащих элементы управления ActiveX) или DRV (для устаревших системных драйверов). Форматы файлов DLL такие же, как и для файлов Windows EXE, то есть, переносимый исполняемый (PE) для 32-разрядных и 64-разрядных версий Windows и новый исполняемый (NE) для 16-разрядной Windows. Как и EXE-файлы, DLL могут содержать код, данные и ресурсы в любом сочетании. Файлы данных, имеющие тот же формат файла, что и DLL, но с другими расширениями и, возможно, содержащие только разделы ресурсов, могут называться ресурсными DLL. Примеры таких DLL включают многоязычные библиотеки пользовательского интерфейса с расширением MUI, библиотеки значков, иногда имеющие расширение ICL, и файлы шрифтов с расширениями FON и FOT.

Unix-подобные системы, использующие ELF, и системы на основе Дарвина

В большинстве Unix-подобных систем, большая часть машинного кода, составляющего динамический компоновщик, фактически является внешней исполняемой программой, которую ядро операционной системы загружает и выполняет первой в адресном пространстве процесса, только что созданном в результате вызова функций exec или posix spawn. Во время компоновки путь к динамическому компоновщику, который следует использовать, встраивается в исполняемый файл. Когда исполняемый файл загружается, ядро операционной системы считывает из него путь к динамическому компоновщику и затем пытается загрузить и выполнить этот другой исполняемый файл; если эта попытка не удается, например, если файл по указанному пути не найден, попытка выполнить исходный исполняемый файл также завершается неудачей. Динамический компоновщик затем загружает начальный исполняемый файл и все динамически связанные библиотеки, от которых он зависит, и запускает исполняемый файл. Таким образом, путь к динамическому компоновщику является частью прикладного бинарного интерфейса операционной системы.

Системы, использующие ELF

В Unix-подобных системах, использующих ELF для исполняемых файлов и динамических библиотек, таких как Solaris, 64-битные версии HP-UX, Linux, FreeBSD, NetBSD, OpenBSD и DragonFly BSD, путь к динамическому связчику, который следует использовать, внедряется во время компоновки в секцию interp сегмента PT INTERP исполняемого файла. В этих системах динамически загружаемые общие библиотеки идентифицируются по суффиксу имени файла .so (shared object – общий объект). На поведение динамического связчика можно повлиять как во время выполнения программы, так и во время её компоновки, примеры этого можно найти в справочных страницах динамического связчика для различных Unix-подобных систем. Типичным изменением этого поведения является использование переменных среды LD_LIBRARY_PATH и LD_PRELOAD, которые корректируют процесс связывания во время выполнения, осуществляя поиск общих библиотек в альтернативных местах и принудительно загружая и связывая библиотеки, которые в противном случае не были бы загружены, соответственно. Примером является zlibc, также известная как uncompress.so, которая обеспечивает прозрачную декомпрессию при использовании через "трюк" LD_PRELOAD; как следствие, можно читать предварительно сжатые (gzip) файлы в системах BSD и Linux так, как будто они не сжаты, фактически позволяя пользователю добавить прозрачную декомпрессию в базовую файловую систему, хотя и с некоторыми ограничениями. Механизм достаточно гибок, позволяя легко адаптировать тот же код для выполнения дополнительной или альтернативной обработки данных во время чтения файла, до того как данные будут предоставлены пользовательскому процессу, который их запросил.

macOS и iOS

В операционной системе Apple Darwin, а также в операционных системах macOS и iOS, построенных на её основе, путь к динамическому связчику, который следует использовать, внедряется во время компоновки в одну из команд загрузки Mach-O в исполняемом файле. В этих системах динамически загружаемые общие библиотеки могут быть идентифицированы либо суффиксом имени файла `dylib`, либо их размещением внутри пакета фреймворка. Динамический связчик не только связывает целевой исполняемый файл с общими библиотеками, но и размещает функции машинного кода в определенных точках памяти, о которых целевой исполняемый файл знает во время компоновки. Когда исполняемый файл хочет взаимодействовать с динамическим связчиком, он просто выполняет машиноспецифическую инструкцию вызова или перехода к одной из этих известных точек адреса. Исполняемые файлы на платформах macOS и iOS часто взаимодействуют с динамическим связчиком во время выполнения процесса; известно даже, что исполняемый файл может взаимодействовать с динамическим связчиком, вызывая загрузку дополнительных библиотек и разрешение дополнительных символов, спустя часы после первоначального запуска. Причина, по которой программа macOS или iOS так часто взаимодействует с динамическим связчиком, обусловлена как API Apple Cocoa и Cocoa Touch, так и языком Objective-C, на котором они реализованы (подробности см. в соответствующих статьях). Динамический связчик может быть вынужден изменить некоторые свои настройки; однако, в отличие от других Unix-подобных операционных систем, эти изменения являются лишь рекомендациями, которые динамический связчик может (и иногда действительно) игнорировать. Примеры этого можно найти в руководстве по dyld. Типичным изменением настроек является использование переменных окружения `DYLD_FRAMEWORK_PATH` и `DYLD_PRINT_LIBRARIES`. Первая из упомянутых переменных корректирует путь поиска исполняемых файлов для общих библиотек, а вторая отображает имена загружаемых и компонуемых библиотек. Динамический связчик macOS от Apple – это проект с открытым исходным кодом, выпущенный в составе Darwin, и его можно найти в проекте dyld с открытым исходным кодом от Apple.

Unix-подобные системы на базе XCOFF

В Unix-подобных операционных системах, использующих XCOFF, таких как AIX, динамически загружаемые общие библиотеки имеют суффикс имени файла «a». На поведение динамического связывателя можно повлиять как во время выполнения программы, так и во время её компоновки. Типичным способом изменения этого поведения является использование переменной окружения LIBPATH. Эта переменная корректирует процесс связывания во время выполнения, осуществляя поиск общих библиотек в альтернативных каталогах и принудительно загружая и связывая библиотеки, которые в противном случае не были бы загружены и связаны.

OS/360 и преемники

Динамическая связь из программ на языке Assembler в IBM OS/360 и последующих версиях обычно осуществляется с помощью макроинструкции LINK, содержащей инструкцию Supervisor Call, которая активирует подпрограммы операционной системы, обеспечивающие доступность модуля библиотеки для программы. Модули библиотек могут располагаться в "STEPLIB" или "JOBLIB", указанных в управляющих картах и доступных только для конкретного запуска программы, в библиотеке, включенной в LINKLIST в PARMLIB (указанной при запуске системы), или в "области пакета связей", где определенные повторно входящие модули загружаются во время запуска системы.

Мультики

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

Эффективность

Динамическая связь обычно медленнее (требует больше тактов процессора), чем связь во время компиляции, как это и происходит с большинством процессов, выполняемых во время работы. Однако динамическая связь часто более экономична с точки зрения занимаемого места (на диске и в памяти во время выполнения). При статической связи каждый запускаемый процесс содержит собственную копию вызываемых функций библиотеки. Следовательно, если библиотека используется многими разными программами, одни и те же функции в этой библиотеке дублируются в нескольких местах в памяти системы. Использование общих динамических библиотек означает, что вместо того, чтобы связывать каждый файл со своей копией библиотеки во время компиляции и потенциально расходовать память, в памяти одновременно хранится только одна копия библиотеки, освобождая место для других задач. Кроме того, при динамической связи библиотека загружается только в том случае, если она действительно необходима.