Функция dlopen() загружает динамический общий объект (общую библиотеку) из файла, имя которого указано в строке filename (завершается null) и возвращает скрытный описатель на загруженный объект. Данный описатель используется другими функциями программного интерфейса dlopen, такими как dlsym(3), dladdr(3), dlinfo(3) и dlclose().
Если filename равно NULL, то возвращается описатель основной программы. Если filename содержит косую черту («/»), то это воспринимается как имя с путём (относительным или абсолютным). Иначе динамический компоновщик ищет объект в следующих местах (подробности смотрите в ld.so(8)):
- •
- (ELF only) If the calling object (i.e., the shared library or executable from which dlopen() is called) contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag, then the directories listed in the DT_RPATH tag are searched.
- •
- Если при запуске программы была определена переменная окружения LD_LIBRARY_PATH, содержащая список каталогов через двоеточие, то производится поиск в этих каталогах (по соображениям безопасности эта переменная игнорируется для программ с установленными битами set-user-ID и set-group-ID).
- •
- (ELF only) If the calling object contains a DT_RUNPATH tag, then the directories listed in that tag are searched.
- •
- Производится проверка в кэширующем файле /etc/ld.so.cache (обслуживается ldconfig(8)EN) на предмет наличия записи для filename.
- •
- Просматриваются каталоги /lib и /usr/lib (именно в таком порядке).
Если объект, указанный filename, зависит от других общих объектов, то они также автоматически загружаются динамическим компоновщиком согласно этим же правилам (процесс может выполняться рекурсивно, если эти объекты, в свою очередь, зависят от других, и так далее).
В flags должно быть одно из двух следующих значений:
- RTLD_LAZY
- Выполнять позднее связывание (lazy binding). Выполняется поиск только тех символов, на которые есть ссылки из кода. Если на символ никогда не ссылаются, то он никогда не будет разрешён (позднее связывание выполняется только при ссылке на функции; ссылки на переменные всегда привязываются сразу при загрузке общего объекта). Начиная с libc 2.1.1, этот флаг заменяется на значение переменной окружения LD_BIND_NOW.
- RTLD_NOW
- Если указано данное значение или переменная окружения LD_BIND_NOW не пуста, то все неопределённые символы в общем объекте ищутся до возврата из dlopen(). Если этого сделать не удаётся, то возвращается ошибка.
Также в В flags может быть ноль или более значение, объединяемых по ИЛИ:
- RTLD_GLOBAL
- Символы, определённые в этом общем объекте, будут доступны при поиске символов, для общих объектов, загружаемых далее.
- RTLD_LOCAL
- Противоположность RTLD_GLOBAL, используется по умолчанию, если не задано ни одного флага. Символы, определённые в этом общем объекте, не будут доступны при разрешении ссылок для общих объектов, загружаемых далее.
- RTLD_NODELETE (начиная с glibc 2.2)
- Не выгружать общий объект при dlclose(). В результате статические и глобальные переменные объекта не инициализируются повторно, если объект загружается снова по dlopen().
- RTLD_NOLOAD (начиная с glibc 2.2)
- Не загружать общий объект. Это можно использовать для тестирования того, что объект уже загружен (dlopen() возвращает NULL, если нет, или описатель объекта в противном случае). Данный флаг также можно использовать для изменения флагов уже загруженного объекта. Например, общий объект, который был загружен ранее с RTLD_LOCAL, можно открыть повторно с RTLD_NOLOAD | RTLD_GLOBAL.
- RTLD_DEEPBIND (начиная с glibc 2.3.4)
- Задать объекта, в котором поиск символов будет осуществляться перед поиском в области глобальных символов. Это означает, что самодостаточный объект будет использовать свои собственные символы вместо глобальных символов с тем же именем, содержащихся в объектах, которые уже были загружены.
If filename is NULL, then the returned handle is for the main program. When given to dlsym(3), this handle causes a search for a symbol in the main program, followed by all shared objects loaded at program startup, and then all shared objects loaded by dlopen() with the flag RTLD_GLOBAL.
Поиск символьных ссылок общего объекта производится следующим образом (в таком порядке): символы в карте ссылок объектов, загруженных для главной программы и её зависимостей; символы в общих объектах (и их зависимостях), которые были открыты ранее с помощью dlopen() и флага RTLD_GLOBAL; определения в самом общем объекте (и любых зависимостях, которые были загружены для этого объекта).
Все глобальные символы в исполняемом файле, которые были помещены в его таблицу динамических символов посредством ld(1), также могут быть использованы при поиске ссылок динамически загружаемого общего объекта. Символы могут попасть в таблицу динамических символов из-за того, что исполняемый файл был скомпонован с флагом «-rdynamic» (или его синонимом «--export-dynamic»), который помещает что все глобальные символы исполняемого файла в таблицу динамических символов, или из-за того, что ld(1) определил зависимость от символа из другого объекта при статической компоновке.
Если общий объект загружается с помощью dlopen() повторно, то возвращается тот же описатель на объект. Динамический компоновщик ведёт счётчик ссылок для описателей объектов, поэтому динамически загруженный общий объект не высвобождается dlclose() до тех пор, пока он не будет вызвана столько же раз сколько и dlopen(). Процедуры инициализации (смотрите ниже) вызываются только когда объект действительно загружается в память (т. е., когда счётчик ссылок увеличивается на 1).
Последующий вызов dlopen(), загружающий тот же общий объект с флагом RTLD_NOW, может привести к поиску символов для общего объекта ранее загруженного с флагом RTLD_LAZY. Схожим образом объект, открытый ранее с флагом RTLD_LOCAL, в последующем вызове dlopen() может быть преобразован в RTLD_GLOBAL.
Если по какой-то причине dlopen() завершается неудачно, то возвращается NULL.
This function performs the same task as dlopen()—the filename and flags arguments, as well as the return value, are the same, except for the differences noted below.
Функция dlmopen() отличается от dlopen(), главным образом в том, что имеет дополнительный аргумент lmid, в котором задаётся список карт связей (link-map list, ещё называемый пространством имён), в который должен быть загружен общий объект (dlopen() добавляет динамически загружаемый общий объект в тоже пространство имён, в котором находится общий объект, из которого был вызван dlopen()). Тип Lmid_t является скрытым описателем, который ссылается на пространство имён.
В аргументе lmid может быть указан ID существующего пространства имён (который может быть получен с помощью dlinfo(3) с запросом RTLD_DI_LMID) или одно из следующих специальных значений:
- LM_ID_BASE
- Загрузить общий объект в начальное пространство имён (т. е., в пространство имён приложения).
- LM_ID_NEWLM
- Создать новое пространство имён и загрузить в него общий объект. Объект должен быть корректно скомпонован с ссылками на все остальные общие объекты, которые ему требуются, так как новое пространство имён изначально пустое.
Если filename равно NULL, то для lmid разрешено только значение LM_ID_BASE.
Функция dlclose() уменьшает счётчик ссылок на динамически загружаемый общий объект, на который ссылается handle.
Если счётчик ссылок достигает нуля и символы этого объекта не нужны другим объектам, то объект выгружается после первого вызова любого деструктора, определённого в объекте (символы в этом объекте могут требоваться в другом объекте из-за того, что этот объект был открыт с флагом RTLD_GLOBAL и один из его символов совпадает с расположением из другого объекта).
Все общие объекты, которые были автоматически загружены при вызове dlopen() для объекта, на который ссылается handle, рекурсивно закрываются таким же способом.
Успешный возврат из dlclose() не гарантирует, что символы, связанные с handle удалятся из адресного пространства вызывающего. В дополнении к ссылкам, полученным из-за явного вызова dlopen(), общий объект может быть загружен неявно (и увеличится счётчик ссылок), так как от него зависят другие общие объекты. Общий объект будет удалён из адресного пространства только когда будут удалены все ссылки на него.