dlopen()

Загрузить динамическую библиотеку

Прототип:

#include <dlfcn.h>
void * dlopen( const char *pathname,
int mode );

Аргументы:

pathname
NULL или путь к динамической библиотеке, которую необходимо загрузить.
mode
Флаги, управляющие работой dlopen(). POSIX определяет следующие флаги:
RTLD_LAZY
RTLD_NOW
RTLD_GLOBAL
RTLD_LOCAL
Следующие флаги являются расширением Unix:
RTLD_NOLOAD
RTLD_GROUP
RTLD_WORLD
RTLD_NODELETE
Следующие флаги являются расширениями ЗОСРВ «Нейтрино»:
RTLD_NOSHARE
RTLD_LAZYLOAD

Библиотека:

libc

Описание:

Функция dlopen() предоставляет прямой доступ к средствам динамического связывания, делая доступным для вызывающего процесса исполняемый объектный файл, определенный в pathname. Она возвращает дескриптор, который можно использовать в последующих вызовах dlsym() и dlclose().


Note: Функция dlopen() доступна только для процессов с динамическим связыванием. Процессы со статическим связыванием (те, с которыми libc связан статически) не могут вызывать dlopen(), так как статически связанный исполняемый файл:
  • не экспортирует свои символы
  • не может экспортировать требуемую структуру для библиотек для компоновки
  • не может заполнять структуры при запуске, необходимые для загрузки последующих динамических библиотек

Любые зависимости, отмеченные в объекте pathname, загружаются как часть вызова dlopen(). Эти зависимости ищутся в порядке загрузки для определения других дополнительных (рекурсивных) зависимостей. Данный процесс продолжается до полного удовлетворения всех зависимостей из pathname. Это дерево зависимостей называется группой.

Если аргумент pathname равен NULL, dlopen() возвращает дескриптор глобального символьного объекта запущенного процесса. Это обеспечивает доступ к символам из исходного файла программного образа, из зависимостей, загруженных при запуске, а также к любым библиотекам, загруженным с помощью dlopen() с использованием флага RTLD_GLOBAL. Этот набор символов может динамически изменяться, если приложение впоследствии вызовет dlopen() с флагом RTLD_GLOBAL.

Вы можете использовать dlopen() любое количество раз для библиотек, чьи имена указывают на один и тот же абсолютный и относительный путь; данные каждой библиотеки загружаются в адресное пространство процесса только один раз.

Чтобы найти разделяемые библотеки, dlopen() выполняет поиск в следующем порядке:

Следует помнить, что LD_LIBRARY_PATH игнорируется, если для бинарного файла выставлен бит suid, и эффективный и фактический ID пользователя для бинарного файла не совпадают. Это сделано в целях безопасности.


Note: Вышеуказанные директории настроены следующим образом:
  • LD_LIBRARY_PATH обычно устанавливается скриптом запуска, в загрузочном образе или дополнительным скриптом. Например, в скрипте ph (запускает Photon) добавляет в список для поиска библиотек несколько путей. Он не является частью какого-либо стандартного окружения.

  • _CS_LIBPATH заполняется ядром, значение по умолчанию основано на значении LD_LIBRARY_PATH командной строки procnto-* в загрузочном образе. Следует заметить, что можно использовать getconf для проверки и setconf для установки этого значения. Например:

    setconf _CS_LIBPATH 'getconf _CS_LIBPATH':/new/path



Note: Подробнее процесс загрузчи разделяемых библиотек, разрешения символов и их релокация описаны в статье Динамический загрузчик программ.

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

Режим

Аргумент mode определяет, как dlopen() работает с путями при обработке релокаций, и управляет видимостью символов, найденных в pathname и зависимостях.

Аргумент mode - побитовое ИЛИ констант, описываемых ниже. Обратите внимание, что константы релоцирования и видимости взаимно исключают друг друга.

Релокации (relocation)

При загрузке с помощью функции dlopen() библиотека может содержать ссылки на символы, чьи адреса не известны до того, как библиотека не будет загружена; эти ссылки должны быть релоцированы (relocated) до непосредственного доступа к символам. Аргумент mode контролирует, когда это действие происходит, и может принимать одно из следующих значений:

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

Видимость

Следующие биты mode определяют область видимости символов, загруженных с помощью dlopen():

RTLD_GLOBAL
Делает глобальные символы библиотеки доступными для любой другой библиотеки, открытой позже с флагом RTLD_WORLD. Поиск символов с помощью dlopen( 0, mode ) и связанного с ним dlsym() также дают возможность найти символы библиотеки.
RTLD_LOCAL
Делает глобальные символы библиотеки доступными только для библиотек в пределах одной и той же группы.
RTLD_LAZYLOAD
Открывает разделяемую библиотеку и формирует его область разрешений, добавляя лишь непосредственные зависимости. Это отличается от обычной области разрешений, которая формируется путем добавления всего дерева зависимостей, развернутого в ширину.

Образ программы и другие библиотеки, загружаемые при старте программы, имеют режим mode RTLD_GLOBAL; аргумент mode по умолчанию для библиотек, полученных с помощью dlopen() имеет значение RTLD_LOCAL. Локальный объект может быть частью зависимостей более, чем одной группы; любая библиотека с mode, равным RTLD_LOCAL, являющаяся зависимостью библиотеки с mode, равным RTLD_GLOBAL, повышается до RTLD_GLOBAL.

Объекты, загруженные с помощью dlopen(), которые требуют релоцирования глобальных символов, могут ссылаться на символы в любой библиотеке, открытой с RTLD_GLOBAL.

Область символов

Следует использовать в качестве mode ИЛИ со следующими значениями, чтобы повлиять на область символов:

RTLD_GROUP
Доступны символы только из связанной группы. Все зависимости между членами группы должны удовлетворяться объектами в группе.
RTLD_WORLD
Доступны только символы из библиотек с RTLD_GLOBAL.

Если не указать ни одного из этих значений, dlopen() использует RTLD_WORLD | RTLD_GROUP.


Note: Если использовать RTLD_WORLD без RTLD_GROUP, dlopen() не загуржает никакие зависимости DLL.

Остальные флаги

Следующие флаги предоставляют управление дополнительными свойствами:

RTLD_NODELETE
Не удалять указанный объект из адресного пространства при вызове dlclose().
RTLD_NOLOAD
Не загружать указанный объект, но вернуть корректный дескриптор, если объект уже существует как часть адресного пространства процесса. Можно указать дополнительные режимы, которые добавятся с помощью битового ИЛИ к текущему режиму библиотеки и ее зависимостей. Данный флаг дает возможность оперделять наличие или продвигать режимы существующей зависимости.
RTLD_NOSHARE
Не предоставлять общий доступ к указанному объекту. Этот флаг вызывает загрузку нескольких экземпляров библиотеки.

Разрешение символов

При разрешении символов в разделяемой библиотеке компоновщик времени выполнения ищет их в таблице динамических символов в следующем порядке:

По умолчанию:

  1. исполняемое приложение
  2. загруженная разделяемая библиотека
  3. библиотеки, определенные в переменной окружения LD_PRELOAD
  4. все остальные разделяемые библиотеки, загруженные с флагом RTLD_GLOBAL

Когда установлена опция -Bsymbolic:

  1. загруженная разделяемая библиотека
  2. исполняемое приложение
  3. библиотеки, определенные в переменной окружения LD_PRELOAD
  4. все остальные разделяемые библиотеки, загруженные с флагом RTLD_GLOBAL

Для исполняемых файлов таблица динамических символов обычно содержит те символы, для которых известно, что они необходимы каким-либо разделяемым библиотекам. Это определяется компоновщиком во время связывания исполняемого файла с разделяемой библиотекой.

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

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

qcc -Vgcc_ntox86 -Wl,-E -o main main.o

Разделяемые библиотеки обычно размещают все свои символы в таблице динамических символов, поэтому данная опция не нужна при компоновке разделяемых библиотек. Для ограницения области видимости в динамических библиотеках могут применяться скрипты версионирования (version-scripts) и атрибуты видимости компилятора вроде attribute((visibility("default"))).

Возвращаемое значение:

Дескриптор библиотеки или NULL в случае ошибки.


Note: Не следует как-то интерпретировать значение этого дескриптора. Например, при повторном открытии библиотеки не следует рассчитывать, что dlopen() вернет тот же дескриптор.

Коды ошибок:

При возникновении ошибок детальную информацию можно получить, используя функцию dlerror().

Переменные окружения:

DL_DEBUG
Если эта переменная окружения установлена, загрузчик динамической библиотеки отображает отладочную информацию о библиотеках, которые были открыты. Значение переменной может быть списком следующих значений, разделенных запятыми: Значение 1 (единица) аналогично all.
LD_BIND_NOW
Влияет на зависимости отложенной загрузки из-за полного разрешения символов. Обычно вызывает загрузку всех зависимостей с отложенной загрузкой (до тех пор, пока не произойдет разрешение всех символов).
LD_DEBUG
Синоним DL_DEBUG. Если установлены и DL_DEBUG, и LD_DEBUG, DL_DEBUG будет более приоритетной.
LD_DEBUG_OUTPUT
Имя файла, в который динамический компоновщик будет осуществлять вывод. По умолчанию, вывод идет в stderr.
LD_LIBRARY_PATH
Разделенный двоеточиями список директорий для поиска разделяемых библиотек.
LD_PRELOAD
Список абсолютных путей к разделяемым библиотекам, которые необходимо загрузить перед загрузкой остальных библиотек. Используйте двоеточие (:) для разделения библиотек в этом списке. Данную переменную окружения можно использовать для добавления или изменения функциональности программы. Двоичные файлы, для которых вызван setuid или setgid, могут загрузить только те библиотеки из стандартных директорий поиска, для которых также был вызван setuid.

Классификация:

POSIX 1003.1 X/Open Systems Interfaces Extension

Безопасность использования
Точка остановки потока
Да
Обработчик прерываний
Нет
Обработчик сигналов
Нет
В потоке
Да

Предостережения:

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

Тематические ссылки:

Динамический загрузчик программ

procnto-*, dladdr(), dlclose(), dlerror(), dlsym()

ld, qcc в Справочнике по Утилитам




Предыдущий раздел: Описание API системной библиотеки