Dlopen error handling

man dlopen (3): открывает и закрывает общий объект

Other Alias

dlclose, dlmopen

ОБЗОР

#include <dlfcn.h>

void *dlopen(const char *filename, int flags);

int dlclose(void *handle);

#define _GNU_SOURCE

#include <dlfcn.h>

void *dlmopen (Lmid_t lmid, const char *filename, int
flags);

Компонуется при указании параметра -ldl.

ОПИСАНИЕ

dlopen()

Функция dlopen() загружает динамический общий объект (общую библиотеку)
из файла, имя которого указано в строке filename (завершается null) и
возвращает непрозрачный описатель на загруженный объект. Данный описатель
используется другими функциями программного интерфейса dlopen, такими как
dlsym(3), dladdr(3), dlinfo(3) и dlclose().

Если filename равно NULL, то возвращается описатель основной
программы. Если filename содержит косую черту («/»), то это
воспринимается как имя с путём (относительным или абсолютным). Иначе
динамический компоновщик ищет объект в следующих местах (подробности
смотрите в ld.so(8)):

  • (только в ELF) Если исполняемый файл вызывающей программы содержит метку
    DT_RPATH, т не содержит метки DT_RUNPATH, то производится поиск в каталогах,
    описанных в метке DT_RPATH.
  • Если при запуске программы была определена переменная окружения
    LD_LIBRARY_PATH, содержащая список каталогов через двоеточие, то
    производится поиск в этих каталогах (по соображениям безопасности эта
    переменная игнорируется для программ с установленными битами set-user-ID и
    set-group-ID).
  • (только в ELF) Если исполняемый файл вызывающей программы содержит метку
    DT_RUNPATH, то производится поиск по каталогам, перечисленным в этой метке.
  • Производится проверка в кэширующем файле /etc/ld.so.cache (обслуживается
    ldconfig(8)) на предмет наличия записи для 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)
Задать объекта, в котором поиск символов будет осуществляться перед поиском
в области глобальных символов. Это означает, что самодостаточный объект
будет использовать свои собственные символы вместо глобальных символов с тем
же именем, содержащихся в объектах, которые уже были загружены.

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

Внешние ссылки в общем объекте разрешаются с использованием общих объектов
по их списку объектных зависимостей и других объектов, ранее открытых с
флагом RTLD_GLOBAL. Если исполняемый файл скомпонован с параметром
«-rdynamic» (или тождественным ему «—export-dynamic»), то глобальные
символы в исполняемом файле будут также использоваться при разрешении
зависимостей в динамически загружаемом общем объекте.

Если данный общий объект загружается с помощью dlopen() снова, то
возвращается тот же описатель на объект. Динамический компоновщик ведёт
счётчик ссылок для описателей объектов, поэтому динамически загруженный
общий объект не высвобождается dlclose() до тех пор, пока он не будет
вызвана столько же раз сколько и dlopen(). Процедура инициализации, если
есть, вызывается только однажды. Но последующий вызов dlopen(),
загружающий тот же общий объект с флагом RTLD_NOW, может привести к
поиску символов для общего объекта ранее загруженного с флагом RTLD_LAZY.

Если по какой-то причине dlopen() завершается неудачно, то возвращается
NULL.

dlmopen()

Данная функция делает то же самое что и dlopen(), аргументы filename и
flags, а также возвращаемое значение — такие же, отличия описаны далее.

Функция 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()

Функция dlclose() уменьшает счётчик ссылок на динамически загружаемый
общий объект, на который ссылается handle. Если счётчик ссылок достигает
нуля, то объект выгружается. Все общие объекты, которые были автоматически
загружены при вызове dlopen() для объекта, на который ссылается
handle, рекурсивно закрываются таким же способом.

Успешный возврат из dlclose() не гарантирует, что символы, связанные с
handle удалятся из адресного пространства вызывающего. В дополнении к
ссылкам, полученным из-за явного вызова dlopen(), общий объект может быть
загружен неявно (и увеличится счётчик ссылок), так как от него зависят
другие общие объекты. Общий объект будет удалён из адресного пространства
только когда будут удалены все ссылки на него.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

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

При успешном выполнении dlclose() возвращает 0; при ошибке возвращается
ненулевое значение.

Ошибки, возникшие в этих функциях, можно определить с помощью dlerror(3).

ВЕРСИИ

Функции dlopen() и dlclose() имеются в glibc 2.0 и новее. Функция
dlmopen() впервые появилась в glibc 2.3.4.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
dlopen(),
dlmopen(),
dlclose()
безвредность в нитях безвредно (MT-Safe)

СООТВЕТСТВИЕ СТАНДАРТАМ

В POSIX.1-2001 описаны dlclose() и dlopen(). Функция dlmopen()
является расширением GNU.

Флаги RTLD_NOLOAD, RTLD_NODELETE и RTLD_DEEPBIND являются
расширением GNU; первые два этих флага есть также в Solaris.

ЗАМЕЧАНИЯ

Функция dlmopen() и пространства имён

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

Функция dlmopen() позволяет достичь изоляции загружаемых объектов —
загружает общий объект в новое пространство имён без показа символов всему
приложению, а только новому объекту. Заметим, что использование флага
RTLD_LOCAL недостаточно для этой цели, так как он делает недоступным
символы общего объекта любому другому общему объекту. В некоторых случаях
может понадобиться, чтобы символы динамически загружаемого общего объекта
были доступны другим общим объектам (но не всем объектам) без показа этих
символов всему приложению. Этого можно достичь используя отдельное
пространство имён и флаг RTLD_GLOBAL.

Функцию dlmopen() также можно использовать для получения изолированности,
большей чем с флагом RTLD_LOCAL. В частности, общие объекты, загруженные
с RTLD_LOCAL, могут быть видимы при флаге RTLD_GLOBAL, если они
зависят от другого общего объекта, загруженного с флагом RTLD_GLOBAL. То
есть, RTLD_LOCAL недостаточно изолирует загружаемый общий объект, за
исключением случая (редкого), где он явно контролирует зависимости всех
загружаемых общих объектов.

Возможный случай применения dlmopen() — модули, где автор инфраструктуры
модулей не может доверять авторам модулей и не хочет, чтобы все
неопределённые символы инфраструктуры модулей определялись из
модулей. Другой случай использования — загрузка одного объекта несколько
раз. Без dlmopen() это потребовало бы создание отдельных копий файлов
общего объекта. С помощью dlmopen() можно загрузить один файл общего
объекта в разные пространства имён.

В реализации glibc поддерживается до 16 пространств имён.

Функции инициализации и завершения

Общие объекты могут экспортировать с помощью атрибутов функций
__attribute__((constructor)) и
__attribute__((destructor)). Функции-конструкторы выполняются перед
возвратом из dlopen(), а функции-деструкторы выполняются перед возвратом
из dlclose(). Общий объект может экспортировать несколько конструкторов и
деструкторов, с каждой функцией может быть связан приоритет, которым
определяется порядок выполнения функций. Подробней смотрите info-страницу
gcc (раздел «Атрибуты функции»).

Старым способом достижения того же (частично) результата является
использование двух специальных символов, распознаваемых компоновщиком:
_init и _fini. Если динамически загружаемый общий объект экспортирует
процедуру с именем _init(), то её код выполняется после загрузки общего
объекта, но возвращения из dlopen(). Если общий объект экспортирует
процедуру с именем _fini(), то её код выполняется перед выгрузкой
объекта. В этом случае не должна выполняться компоновка с системными файлами
начального запуска, в которых содержатся версии по умолчанию этих файлов;
для этого нужно вызывать gcc(1) с параметром командной строки
-nostartfiles.

Использование _init и _fini теперь не рекомендуется, используйте
упомянутые конструкторы и деструкторы, которые, среди прочих преимуществ,
позволяют определять многократно вызываемые функции инициализации и
завершения.

Начиная с glibc 2.2.3, atexit(3) может использоваться для регистрации
обработчика завершения работы, который автоматически вызывается при выгрузке
общего объекта.

История

Эти функции являются часть программного интерфейса dlopen, возникшего в
SunOS.

ПРИМЕР

Программа, представленная ниже, загружает библиотеку math (glibc), ищет
адрес функции cos(3) и печатает косинус 2.0. Пример сборки и выполнения
программы:

$ cc dlopen_demo.c -ldl
$ ./a.out
-0.416147

Исходный код программы

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <gnu/lib-names.h>  /* определение LIBM_SO (который
                               является строкой вида libm.so.6») */
int
main(void)
{
    void *handle;
    double (*cosine)(double);
    char *error;
    handle = dlopen(LIBM_SO, RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%sn", dlerror());
        exit(EXIT_FAILURE);
    }
    dlerror();    /* Очистка всех результатов ошибок */
    cosine = (double (*)(double)) dlsym(handle, "cos");
    /* Согласно стандарту ISO C, преобразование между указателями на
       функции и «void *», использовавшемуся выше, приводит к
       неопределённым результатам. В POSIX.1-2003 и POSIX.1-2008
       принимается такое поведение и предлагается следующий
       обходной вариант:
           *(void **) (&cosine) = dlsym(handle, "cos");
       Такое (топорное) преобразование удовлетворяет стандарту ISO C и
       предупреждений компилятора не будет.
       Список опечаток 2013 к POSIX.1-2008 (т.н. POSIX.1-2013) улучшает
       состояние, требуя от реализаций поддержки преобразования «void *»
       в указатель на функцию. Тем не менее, некоторые компиляторы
       (например, gcc с параметром «-pedantic») могут выдавать
       предупреждение о преобразовании в этой программе. */
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "%sn", error);
        exit(EXIT_FAILURE);
    }
    printf("%fn", (*cosine)(2.0));
    dlclose(handle);
    exit(EXIT_SUCCESS);
}

ДЕФЕКТЫ

В glibc 2.21, указание флага RTLD_GLOBAL при вызове dlmopen() приводит
к ошибке. Кроме этого, указание RTLD_GLOBAL при вызове dlopen()
приводит к падению программы (SIGSEGV), если вызов делается из любого
объекта, загруженного в пространство имён, отличное от начального
пространства имён.

������������� ������� ��������� ��������� ���������� (man-��)

dlopen (3)


  • dlopen (3) ( Solaris man: ������������ ������ )
  • dlopen (3) ( FreeBSD man: ������������ ������ )
  • >> dlopen (3) ( ������� man: ������������ ������ )
  • dlopen (3) ( Linux man: ������������ ������ )
  • dlopen (3) ( POSIX man: ������������ ������ )
  •  

    ��������

    dlclose, dlerror, dlopen, dlsym — ����������� ��������� ��� ����������� ����������� ���������
     

    ���������

    #include <dlfcn.h>

    void *dlopen(const char *filename, int flag);

    const char *dlerror(void);

    void *dlsym(void *handle, char *symbol);

    int dlclose(void *handle);

    ����������� �������:
    _init, _fini.

     

    ��������

    dlopen

    ��������� ������������ ����������, ��� ������� ������� � ������
    filename,

    � ���������� ������ ��������� �� ������ ������������ ����������. ����
    filename

    �� �������� ������ ������ ����� (�.�. �� ���������� � «/»),
    �� ���� ������ � ��������� ������:

    � ����������� ���������� ������ ���������, � ���������� ��������� ������������
    LD_LIBRARY_PATH.

    � ������ ���������, ������������ � ����� /etc/ld.so.cache.

    /usr/lib � ����� � /lib.

    ����
    filename

    ��������� �� NULL, �� ������������ ��������� �� �������� ���������.

    ������� ������ ����������� ��� ������ � ������������
    � ������ ������������ ������������ � ������ �������,
    ������� ��� ������� � ������
    RTLD_GLOBAL.

    ���� ����������� ������ ��� ������ � ������ «-rdynamic», ���
    ���������� ���������� ����� ������������ ��� ���������� ������ �
    ����������� ����������� �����������.

    ��������
    flag

    ������ ���� ����� �� ����:
    RTLD_LAZY,

    ��������������� ���������� �������������� �������� � ���� ����,
    ������������� � �����������
    ������������ ����������; ���
    RTLD_NOW,

    ��������� ���������� ���� �������������� �������� ����� ��������� �� ��
    dlopen

    � ������������ ������, ���� ���������� �� ����� ���� ���������.
    ����� ��������
    RTLD_GLOBAL

    ����� ���� ������ ����� OR ������ �
    flag;

    � ���� ������ ������� �������, ������������ � ����������, ����� ��������
    ����������� ����� �����������.

    ���� ���������� �������������� �������, ���������
    _init,

    �� �� ��� ����������� ����� ��������� �� dlopen.
    ���� ���������� �������� �������� � ��������� ������ ��������,
    �� ��� ����� �������, ������ gcc �������� «-nostartfiles»
    � ��������� ������.

    ���� ���� � �� �� ���������� ����������� ������ �����
    dlopen(),

    �� ������������ ���� � ��� �� ���������. ���������� dl ������������ �������
    ������, ��������� ��� ���������� �� �����, ��� ��� ������������ ���������� �� �����
    ���� ���������, ���� �� ����� ������� �������
    dlclose

    ������� ���, ������� ���������� ��� ��������� ���������� �������
    dlopen .

    ���� �� �����-���� ������� ����������
    dlopen

    ��������, �� ��� ���������� �������� NULL.
    �������� ������������ �����, ����������� ����������� ������, ������������
    ��� ���������� ����� ������� dl (dlopen, dlsym or dlclose), ����� ����
    ������� ��� ������ �������
    dlerror().

    dlerror

    ���������� NULL, ���� �� �������� ������ � ������� ������������� ���
    ��� ���������� ������. ���� ��������
    dlerror()

    ������, �� �� ������ ��� ��������� ���������� ������ ����� ����� NULL.
    dlsym

    ���������� ��������� �� ������������ ����������, ������������ dlopen, �
    �������������� ����� ���������� ���, � ����� ���������� �����, �����������,
    ������ ����������� ���� ������. ���� ������ �� ������, �� ������������ ���������
    dlsym

    �������� NULL; ��� �� �����, ���������� �������� ��������
    dlsym

    �� ������� ������ �������� ���������� � ���������� ���������� ����������
    dlerror,

    � ����� ��������, ����� �� ��� �������� NULL.
    ��� �������� ������, ��� �������� ������� ������������� ����� ���� NULL.
    ����� ���������� ��������� ���������
    dlerror

    � ����������, ��������� ���� �������
    dlerror

    ������� ��� ���, �� ������������ �� �������� ����� ����� NULL.

    ������� ��� ����������� ������-��������� ������������ ���������
    RTLD_DEFAULT


    RTLD_NEXT.

    ������ ���� ��������� ������, ��������� ������� ������ ��������� �� ���������.
    ������ ������ ������� � ��������� ���������� � ������������ � �������� ������.
    �� ������� ������ ��� ������������� �� ������������ ����������. ��� ���������
    ������������� ������� (wrapper) ������ ������� �� ������ ���������.

    dlclose

    ��������� �� ������� ������� ������ �� ��������� ������������ ����������
    handle.

    ���� ��� ������ �����������
    ���������, ������������ �� ������� � ���� ������� ������
    ��������� ������� ��������, �� ������������ ����������
    �����������. ���� ������������ ���������� ������������� �������, ���������
    _fini,

    �� ��� ������� ���������� ����� ��������� ����������.
     

    ������������ ��������

    dlclose

    ���������� 0 ��� ������� ���������� � ��������� ��������� ��� ������.
     

    ������� �������������

    �������� ���������� math � ����� cosine (2.0):

    #include <stdio.h>
    #include <dlfcn.h>
    int main(int argc, char **argv) {
        void *handle;
        double (*cosine)(double);
        char *error;
        handle = dlopen ("/lib/libm.so", RTLD_LAZY);
        if (!handle) {
            fputs (dlerror(), stderr);
            exit(1);
        }
        cosine = dlsym(handle, "cos");
        if ((error = dlerror()) != NULL)  {
            fprintf (stderr, "%sn", error);
            exit(1);
        }
        printf ("%fn", (*cosine)(2.0));
        dlclose(handle);
    }
    

    ���� ���� ������ ��������� � ����� «foo.c», �� ������ ������� ��������� ��� ������
    ��������� �������:

    gcc -rdynamic -o foo foo.c -ldl

    ����������, �������������� _init() � _fini(), ����� ��������������
    ��� ���, �� ������� ����� bar.c:

    gcc -shared -nostartfiles -o bar bar.c

     

    ���������

    ������� RTLD_DEFAULT � RTLD_NEXT ����������
    <dlfcn.h>

    ������ ����� ���� ���������� _GNU_SOURCE ����� ��� ����������.
     

    ���������� �� �������

    �������� ���������� dlopen ������ �� SunOS.

     

    ��. �����

    ld(1),

    ld.so(8),

    ldconfig(8),

    ldd(1),

    ld.so.info


     

    Index

    ��������
    ���������
    ��������
    ������������ ��������
    ������� �������������
    ���������
    ���������� �� �������
    ��. �����

     Динамически загружаемые библиотеки — это библиотеки, которые загружаются не при запуске программы. Они особенно полезны для реализации плагинов или модулей, потому что они позволяют выполнить загрузку плагина тогда, когда он действительно нужен. Например, система подключаемых модулей аутентификации (PAM) использует DL библиотеки, чтобы позволить администраторам настраивать и перенастраивать аутенфикацию. Они также полезны для реализации интерпретаторов, которым время от времени требуется компилировать свой код в машинный код, а затем использовать скомпилированную версию кода с целью повышения эффективности, и все это без остановки. Например, такой подход может быть полезен для реализации JIT-компиляторов или многопользовательского мира (MUD).

     В Linux DL библиотеки в действительности не являются чем-то особенным с точки зрения формата; они строятся как обычные объектные файлы или обычные общие библиотеки, описанные ранее. Главным отличием является то, что эти библиотеки не загружаются автоматически при компоновке или старте программы; вместо этого существует API для открытия, просмотри символов, обработки ошибок и закрытия библиотеки. Пользователям языка Си необходимо подключить заголовочный файл <dlfcn.h> для использования этого API.

     Интерфейс, используемый в Linux, практически такой же, как в Solaris, который мы назовем «dlopen()» API. Тем не менее, этот интерфейс поддерживается не всеми платформами; HP-UX использует другой механизм shl_load(), а платфорсы Windows используют библиотеки DLL с полностью другим интерфейсом. Если вашей целью является широкая переносимость, вам, вероятно, следует подумать об использовании некоторой библиотеки-обёртки, которая скроет различия между платформами. Одним из подходов является библиотека glib с поддержкой динамической загрузки модулей; она использует основные процедуры динамической загрузки конкретной платформы для реализации переносимого интерфейса для этих функций. Если вам необходима большая функциональность, чем эта, вы можете посмотреть на CORBA Object Request Broker (ORB). Если вы все еще заинтересованы в непосредственном использовании интерфейса, поддерживаемого в Linux и Solaris, читайте дальше.

     Разработчики, использующие C++ и динамически загружаемые библиотеки, также должны обратиться к «C++ dlopen mini-HOWTO».

     dlopen()


     Функция dlopen(3) открывает библиотеку и подготавливает ее к использованию. Её прототип на языке Си:

    void * dlopen(const char *filename, int flag);

     Если filename начинается с «/» (т.е. абсолютный путь), dlopen() просто попробует использовать её (не будет производить поиск библиотеки). Иначе, dlopen() будет искать библиотеку в следующем порядке:

    1. Список директорий, разделенных двоеточием, в пользовательской переменной окружения LD_LIBRARY_PATH.

    2. Список библиотек, указанных в /etc/ld.so.cache (который генерируется на основе /etc/ld.so.conf).

    3. Директория /lib, а затем /usr.lib. Обратите внимание на этот порядок; он является обратным порядку, используемому старым загрузчиком a.out. Старый загрузчик a,out при загрузке программ сначала производит поиск в /usr/lib, затем в /lib (смотрите ld.so(8)). Это обычно не должно иметь значения, поскольку библиотека должна находиться либов одном каталоге, либов другом (никогда в обоих), а различные библиотеки с одинаковыми именами это катастрофа.

     В dlopen() значение flag должно быт либо RTLD_LAZY, что значит «разрешить неопределенные символы при выполнении кода из динамической библиотеки», либо RTLD_NOW, что означает «разрешить все неопределенные символы до завершения dlopen() и вернуть ошибку, если это не может быть сделано». RTLD_GLOBAL опционально может быть с любым значением flag, что означает, что внешние символы, определенные в библиотеке, будут доступны для последующих загруженных библиотек. Во время отладки вы ,вероятно, захотите использовать RTDL_NOW; использование RTLD_LAZY может привести к загадочным ошибкам, если есть неразрешенные ссылки. Использование RTLD_NOW делает открытие библиотеки дольше (но это ускоряет поиск позже); если это вызывает проблемы с пользовательским интерфейсом, вы можете переключиться на RTLD_LAZY позже.

     Если одна библиотека зависит от другой (т.е. X зависит от Y), то вам необходимо сперва загрузить зависимости (в этом примере, сперва загрузить Y, а затем X).

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

     На старых системах, если билиотека экспортирует процедуру с именем _init, то этот код выполняется до выхода из dlopen(). Вы можете использовать этот факт в ваших собственных библиотеках для инициализации процедур инициализации. Однако, библиотеки не должны экспортировать процедуры с именами _init или _fini. Этот механизм является устаревшим и может привести к неопределенному поведению. Вместо этого. библиотеки должны экспортировать процедуры, используя атрибуты функции __attribute__((constructor)) и __attribute__((destructor)) (предполагается, что вы используете gcc). Смотрите секцию Конструктор и деструктор библиотеки для дополнительной информации.

     dlerror()


     Ошибки могут быть получены вызовом dlerror(), который возвращает строку, описывающую ошибку последнего вызова dlopen(), dlsym() или dlclose(). Одна странность заключается в том, что после вызова dlerror() следующие вызовы dlerror() будут возвращать NULL, пока не будет обнаружена какая-либо ошибка.

     dlsym()


     Нет смысла загружать DL библиотеку, если вы не можете ее использовать. Главная процедура для использования DL библиотеки это dlsym(3), которая ищет значение символа в данной (открытой) библиотеке. Эта функция определена как:

    void * dlsym(void *handle, char *symbol);

     handle является значением, возвращаемым из dlopen, а symbol это нуль-терминированная строка. Если вы можете избежать этого, не храните результат dlsym() в указателе типа void*, иначе вам придется выполнять приведение типов при каждом использовании (и вы дадите меньше информации другим разработчикам, пытающимся поддерживать программу).

     dlsym() будет возвращать NULL, если символ не найден. Если вы знаете, что символ никогда не принимает значение NULL или 0, это может быть хоршо, однако существует потенциальная неоднозначность в противном случае: если вы получили NULL, значит ли это, что нет такого символа, или этот NULL является значением символа? Стандартным решением является предварительный вызов dlerror() (для очистки какой-либо ошибки, которая могла существовать), затем вызов dlsym() для получения символа, а затем повторный вызов dlerror(), чтобы посмотреть, не произошла ли ошибка. Фрагмент кода будет выглядеть так:

    dlerror(); /* clear error code */
     s = (actual_type) dlsym(handle, symbol_being_searched_for);
     if ((err = dlerror()) != NULL) {
      /* handle error, the symbol wasn't found */
     } else {
      /* symbol found, its value is in s */
     }

     dlclose()


     Функция ldclose(), которая закрывает DL библиотеку, является обратной функции dlopen(). Dl библиотека поддерживает счетчик ссылок для динамических файловых дескрипторов, так что динамическая библиотека в действительности не выгружается, пока dlclose не будет вызвана для нее столько же раз, сколько было успешных вызовов dlopen для этой библиотеки. Следовательно, многократная загрузка библиотеки одной и той же программой не является проблемой. Если библиотека выгружена, вызывается ее функция _fini (при наличии) для старых библиотек, но _fini является устаревшим механизмом, на который не следует полагаться. Вместо этого библиотеки должны экспортировать процедуры, используя фтрибуты функции __attribute__((constructor)) и __attribute__((destructor)). Смотрите секцию Конструктор и деструктор библиотеки для дополнительной информации. Примечание: dlclose() возвращает 0 в случае успеха и ненулевое значение в случае ошибки; некоторые страницы руководства Linux не упоминают об этом.

     Пример DL библиотеки


     Вот пример из справочной страницы dlopen(3). Этот пример загружает библиотеку math и печатает косинус 2.0, также он проверяет ошибки на каждом шаге (рекомендуется):

    #include <stdlib.h>
    #include <stdio.h>
    #include <dlfcn.h>
    
    int main(int argc, char **argv) {
      void *handle;
      double (*cosine)(double);
      char *error;
    
      handle = dlopen ("/lib/libm.so.6", RTLD_LAZY);
      if (!handle) {
        fputs (dlerror(), stderr);
        exit(1);
      }
    
      cosine = dlsym(handle, "cos");
      if ((error = dlerror()) != NULL)  {
        fputs(error, stderr);
        exit(1);
      }
    
      printf ("%fn", (*cosine)(2.0));
      lclose(handle);
    }

     Если бы эта программа находилась в файле с именем «foo.c», вы бы могли собрать программу с помощью следующей команды:

    gcc -o foo foo.c -ldl

    Этот раздел является переводом руководства Program Library HOWTO


    • Start Date: 2020-03-03
    • RFC PR: #9
    • CppMicroServices Issue: TBD

    Shared library loading error handling in Declarative Services

    Summary

    One paragraph explanation of the feature.

    Provide a means for clients to know when a service implemented using Declarative Services could not be retrieved due to a failure to load the bundle’s shared library. Such exceptional conditions are caught internally by Declarative Services and logged. Such exceptional behavior should be visible to clients similar to how starting a bundle using Bundle::Start when using Bundle Activators will throw an exception if the bundle’s shared library cannot be loaded.

    Motivation

    Why are we doing this? What use cases does it support? What is the expected
    outcome?

    When moving bundle implementations from using Bundle Activators to Declarative Services with lazy loading of the bundle’s shared library, failures from loading the shared library will not occur until the service is retrieved, i.e. when BundleContext::GetService() is called. When the shared library fails to load the result is that BundleContext::GetService() returns a nullptr. It’s not clear whether the nullptr is a result of the shared library failing to load or by some other means.

    Declarative Services does log more detailed information about the source of the failure however this is disconnected from the point at which the failure actually occurred.

    The problem is that it becomes more difficult to add error handling mechanisms which make it easy for developers to pinpoint the source of shared library loading errors when using Declarative Services.

    Use Case 1

    setup_system.cpp — code to setup the system, allowing GetService to be called by other clients

    // get the framework
    
    // install and start a bundle which will be used by other clients later in the lifetime of the system
    
    // for the purposes of this use case, this bundle's shared library has missing link-time dependencies which will cause dlopen/LoadLibrary to fail.
    auto bundles = framework.GetBundleContext().InstallBundles("bundlefoo.dll");
    
    try {
      // Start() will not throw, it will appear that there are no failures.
      bundles.front().Start();
    } catch(...) {
      // display meaningful message to users.
      // Nothing will be caught if the bundle is using DS.
    }

    client.cpp

    // get the framework
    
    // DS will provide a valid ServiceReference before it even tries to load the shared library. At this point, it isn't known whether the shared library will fail to load or not.
    auto svcRef = framework.GetBundleContext().GetServiceReference<Foo>();
    
    // This will return a nullptr if the shared library failed to load.
    auto svc = framework.GetBundleContext().GetService(svcRef);
    
    if(nullptr == svc) {
      // ...  error handling if svc == nullptr? ... don't know the source nor context of the failure...
      // how does the client code report a meaningful error message?
    }

    Detailed design

    This is the bulk of the RFC.

    Explain the design in enough detail for somebody
    familiar with the framework to understand, and for somebody familiar with the
    implementation to implement. This should get into specifics and corner-cases,
    and include examples of how the feature is used. Any new terminology should be
    defined here.

    Overview

    • Create a new exception type for shared library load failures — SharedLibraryException. This exception type provides a C++ equivalent of Java ClassNotFoundException that is thrown when the class loader can’t find a class (see OSGi Core Specification section 10.1.5.32 — https://osgi.org/specification/osgi.core/7.0.0/framework.api.html#org.osgi.framework.Bundle)
    • Throw this exception from Bundle::Start and have DS throw it from its ServiceFactory::GetService implementation.
    • Modify CppMicroServices to catch a SharedLibraryException exception, log it per the OSGi spec, and rethrow it.
    • Modify the DS implementation to not catch exceptions thrown from a failure to load the shared library.
    • SharedLibraryException will be constructed with a valid Bundle object representing the shared library which failed to load.

    Functional Design

    namespace cppmicroservices {
    class SharedLibraryException final : public std::system_error
    {
    public:
      explicit SharedLibraryException(std::error_code ec, 
                                      std::string what,
                                      cppmicroservices::Bundle origin);
      ~SharedLibraryException() override;
      Bundle GetBundle() const;
        
    private:
      Bundle origin;  ///< The bundle of the shared library which failed to load.
    }
    }

    client.cpp

    // get the framework
    
    // DS will provide a valid ServiceReference before it even tries to load the shared library. At this point, it isn't known whether the shared library will fail to load or not.
    auto svcRef = framework.GetBundleContext().GetServiceReference<Foo>();
    
    // This will throw a SharedLibraryException if the shared library failed to load.
    // either allow the throw to propagate or try/catch and do something
    try {
      auto svc = framework.GetBundleContext().GetService(svcRef);
    } catch(const cppmicroservices::SharedLibraryException& ex) {
      // do something with ex...
    }
    
    if(nullptr == svc) {
      // do something knowing that the reason this is nullptr is due to a recoverable failure.
    }

    CppMicroServices implementation changes

    • ServiceReferenceBasePrivate::GetServiceFromFactory needs to explicitly catch SharedLibraryException before catching std::exception, log a FrameworkEvent and rethrow.
    • Bundle::Start() needs to throw SharedLibraryException if dlopen/LoadLibrary failed.

    DS implementation changes

    • Declarative Services needs to throw a SharedLibraryException in two cases;
      • When the service component has immediate set to true.
        • Bundle::Start() will throw a SharedLibraryException.
      • When the service component has immediate set to false.
        • BundleContext::GetService() will throw a SharedLibraryException.
        • Declarative Services has try/catch blocks around the operations to enable a service component. These try/catch blocks will have to be modified to catch a SharedLibraryException and rethrow it. The try/catch block starts in SCRActivator::CreateExtension with more try/catch blocks contained within the call chain of this method, ultimately ending in BundleLoader::GetComponentCreatorDeletors where the dlopen/LoadLibrary happens.

    How we teach this

    What names and terminology work best for these concepts and why? How is this
    idea best presented? As a continuation of existing CppMicroServices patterns, or as a
    wholly new one?

    Would the acceptance of this proposal mean the CppMicroServices guides must be
    re-organized or altered? Does it change how CppMicroServices is taught to new users
    at any level?

    How should this feature be introduced and taught to existing CppMicroServices
    users?

    • Requires updates to the doxygen documentation for BundleContext::GetService and Bundle::Start()describing the new exception type which can be thrown.

    Drawbacks

    Why should we not do this? Please consider the impact on teaching CppMicroServices,
    on the integration of this feature with other existing and planned features,
    on the impact of the API churn on existing apps, etc.

    There are tradeoffs to choosing any path, please attempt to identify them here.

    • Increase in code complexity with additional try/catch blocks added to CppMicroServices framework and Declarative Services implementations.

    Alternatives

    What other designs have been considered? What is the impact of not doing this?

    This section could also include prior art, that is, how other frameworks in the same domain have solved this problem.

    Alternative 1

    Implement the LogService callback mechanism and have user code use it to receive the exception object when a shared library fails to load.

    Alternative 2

    Create a BundleContext::GetService overload — GetService(ServiceReference, std::exception_ptr& out) — for callers who want to know about exceptional, unexpected behavior. The second parameter is an out parameter containing the exception thrown from the user implemented ServiceFactory::GetService method.

    Alternative 3

    Modify user code to register a framework listener and do appropriate error handling when a framework event of type Error with an exception type of FactoryException. NOTE: event listeners cannot rethrow from their callback and expect it to propagate. It will be caught by CppMicroServices.

    Unresolved questions

    Optional, but suggested for first drafts. What parts of the design are still
    TBD?

    The name of the exception class, SharedLibraryException, is a work in progress…

    How does OSGi report back failures to load bundle JARs? Can that be applied in C++?

    Command to display dlopen manual in Linux: $ man 3 dlopen

    NAME

    dlclose, dlopen, dlmopen —
    open and close a shared object

    SYNOPSIS

    #include <dlfcn.h>

    void *dlopen(const char *filename, int flags);

    int dlclose(void *handle);

    #define _GNU_SOURCE

    #include <dlfcn.h>

    void *dlmopen (Lmid_t lmid, const char *filename, int flags);

    Link with -ldl.

    DESCRIPTION

    dlopen()

    The function
    dlopen()

    loads the dynamic shared object (shared library)
    file named by the null-terminated
    string
    filename

    and returns an opaque «handle» for the loaded object.
    This handle is employed with other functions in the dlopen API, such as
    dlsym(3),

    dladdr(3),

    dlinfo(3),

    and
    dlclose().

    If
    filename

    is NULL, then the returned handle is for the main program.
    If
    filename

    contains a slash («/»), then it is interpreted as a (relative
    or absolute) pathname.
    Otherwise, the dynamic linker searches for the object as follows
    (see
    ld.so(8)

    for further details):

    o
    (ELF only) If the executable file for the calling program
    contains a DT_RPATH tag, and does not contain a DT_RUNPATH tag,
    then the directories listed in the DT_RPATH tag are searched.
    o
    If, at the time that the program was started, the environment variable
    LD_LIBRARY_PATH

    was defined to contain a colon-separated list of directories,
    then these are searched.
    (As a security measure, this variable is ignored for set-user-ID and
    set-group-ID programs.)

    o
    (ELF only) If the executable file for the calling program
    contains a DT_RUNPATH tag, then the directories listed in that tag
    are searched.
    o
    The cache file
    /etc/ld.so.cache

    (maintained by
    ldconfig(8))

    is checked to see whether it contains an entry for
    filename.

    o
    The directories
    /lib

    and
    /usr/lib

    are searched (in that order).

    If the object specified by
    filename

    has dependencies on other shared objects,
    then these are also automatically loaded by the dynamic linker
    using the same rules.
    (This process may occur recursively,
    if those objects in turn have dependencies, and so on.)

    One of the following two values must be included in
    flags:

    RTLD_LAZY

    Perform lazy binding.
    Resolve symbols only as the code that references them is executed.
    If the symbol is never referenced, then it is never resolved.
    (Lazy binding is performed only for function references;
    references to variables are always immediately bound when
    the shared object is loaded.)
    Since glibc 2.1.1,

    this flag is overridden by the effect of the
    LD_BIND_NOW

    environment variable.

    RTLD_NOW

    If this value is specified, or the environment variable
    LD_BIND_NOW

    is set to a nonempty string,
    all undefined symbols in the shared object are resolved before
    dlopen()

    returns.
    If this cannot be done, an error is returned.

    Zero or more of the following values may also be ORed in
    flags:

    RTLD_GLOBAL

    The symbols defined by this shared object will be
    made available for symbol resolution of subsequently loaded shared objects.
    RTLD_LOCAL

    This is the converse of
    RTLD_GLOBAL,

    and the default if neither flag is specified.
    Symbols defined in this shared object are not made available to resolve
    references in subsequently loaded shared objects.

    RTLD_NODELETE (since glibc 2.2)

    Do not unload the shared object during
    dlclose().

    Consequently, the object’s static and global variables are not reinitialized
    if the object is reloaded with
    dlopen()

    at a later time.

    RTLD_NOLOAD (since glibc 2.2)

    Don’t load the shared object.
    This can be used to test if the object is already resident
    (dlopen()

    returns NULL if it is not, or the object’s handle if it is resident).
    This flag can also be used to promote the flags on a shared object
    that is already loaded.
    For example, a shared object that was previously loaded with
    RTLD_LOCAL

    can be reopened with
    RTLD_NOLOAD | RTLD_GLOBAL.

    RTLD_DEEPBIND (since glibc 2.3.4)

    Place the lookup scope of the symbols in this
    shared object ahead of the global scope.
    This means that a self-contained object will use
    its own symbols in preference to global symbols with the same name
    contained in objects that have already been loaded.

    If
    filename

    is NULL, then the returned handle is for the main program.
    When given to
    dlsym(),

    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.

    Symbol references in the shared object are resolved using (in order):
    symbols in the link map of objects loaded for the main program and its
    dependencies;
    symbols in shared objects (and their dependencies)
    that were previously opened with
    dlopen()

    using the
    RTLD_GLOBAL

    flag;
    and definitions in the shared object itself
    (and any dependencies that were loaded for that object).

    Any global symbols in the executable that were placed into
    its dynamic symbol table by
    ld(1)

    can also be used to resolve references in a dynamically loaded shared object.
    Symbols may be placed in the dynamic symbol table
    either because the executable was linked with the flag «-rdynamic»
    (or, synonymously, «—export-dynamic»), which causes all of
    the executable’s global symbols to be placed in the dynamic symbol table,
    or because
    ld(1)

    noted a dependency on a symbol in another object during static linking.

    If the same shared object is opened again with
    dlopen(),

    the same object handle is returned.
    The dynamic linker maintains reference
    counts for object handles, so a dynamically loaded shared object is not
    deallocated until
    dlclose()

    has been called on it as many times as
    dlopen()

    has succeeded on it.
    Constructors (see below) are called only when the object is actually loaded
    into memory (i.e., when the reference count increases to 1).

    A subsequent
    dlopen()

    call that loads the same shared object with
    RTLD_NOW

    may force symbol resolution for a shared object earlier loaded with
    RTLD_LAZY.

    Similarly, an object that was previously opened with
    RTLD_LOCAL

    can be promoted to
    RTLD_GLOBAL

    in a subsequent
    dlopen().

    If
    dlopen()

    fails for any reason, it returns NULL.

    dlmopen()

    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.

    The
    dlmopen()

    function differs from
    dlopen()

    primarily in that it accepts an additional argument,
    lmid,

    that specifies the link-map list (also referred to as a
    namespace)

    in which the shared object should be loaded.
    (By comparison,
    dlopen()

    adds the dynamically loaded shared object to the same namespace as
    the shared object from which the
    dlopen()

    call is made.)
    The
    Lmid_t

    type is an opaque handle that refers to a namespace.

    The
    lmid

    argument is either the ID of an existing namespace

    (which can be obtained using the
    dlinfo(3)

    RTLD_DI_LMID

    request) or one of the following special values:

    LM_ID_BASE

    Load the shared object in the initial namespace
    (i.e., the application’s namespace).
    LM_ID_NEWLM

    Create a new namespace and load the shared object in that namespace.
    The object must have been correctly linked
    to reference all of the other shared objects that it requires,
    since the new namespace is initially empty.

    If
    filename

    is NULL, then the only permitted value for
    lmid

    is
    LM_ID_BASE.

    dlclose()

    The function
    dlclose()

    decrements the reference count on the
    dynamically loaded shared object referred to by
    handle.

    If the object’s reference count drops to zero
    and no symbols in this object are required by other objects,
    then the object is unloaded
    after first calling any destructors defined for the object.
    (Symbols in this object might be required in another object
    because this object was opened with the
    RTLD_GLOBAL

    flag and one of its symbols satisfied a relocation in another object.)

    All shared objects that were automatically loaded when
    dlopen()

    was invoked on the object referred to by
    handle

    are recursively closed in the same manner.

    A successful return from
    dlclose()

    does not guarantee that the symbols associated with
    handle

    are removed from the caller’s address space.
    In addition to references resulting from explicit
    dlopen()

    calls, a shared object may have been implicitly loaded
    (and reference counted) because of dependencies in other shared objects.
    Only when all references have been released can the shared object
    be removed from the address space.

    RETURN VALUE

    On success,
    dlopen()

    and
    dlmopen()

    return a non-NULL handle for the loaded object.
    On error
    (file could not be found, was not readable, had the wrong format,
    or caused errors during loading),
    these functions return NULL.

    On success,
    dlclose()

    returns 0; on error, it returns a nonzero value.

    Errors from these functions can be diagnosed using
    dlerror(3).

    VERSIONS

    dlopen()

    and
    dlclose()

    are present in glibc 2.0 and later.
    dlmopen()

    first appeared in glibc 2.3.4.

    ATTRIBUTES

    For an explanation of the terms used in this section, see
    attributes(7).

    Interface Attribute Value
    dlopen(),

    dlmopen(),

    dlclose()

    Thread safety MT-Safe

    CONFORMING TO

    POSIX.1-2001 describes
    dlclose()

    and
    dlopen().

    The
    dlmopen()

    function is a GNU extension.

    The
    RTLD_NOLOAD,

    RTLD_NODELETE,

    and
    RTLD_DEEPBIND

    flags are GNU extensions;
    the first two of these flags are also present on Solaris.

    NOTES

    dlmopen() and namespaces

    A link-map list defines an isolated namespace for the
    resolution of symbols by the dynamic linker.
    Within a namespace,
    dependent shared objects are implicitly loaded according to the usual rules,
    and symbol references are likewise resolved according to the usual rules,
    but such resolution is confined to the definitions provided by the
    objects that have been (explicitly and implicitly) loaded into the namespace.

    The
    dlmopen()

    function permits object-load isolation—the ability
    to load a shared object in a new namespace without
    exposing the rest of the application to the symbols
    made available by the new object.
    Note that the use of the
    RTLD_LOCAL

    flag is not sufficient for this purpose,
    since it prevents a shared object’s symbols from being available to
    any

    other shared object.
    In some cases,
    we may want to make the symbols provided by a dynamically
    loaded shared object available to (a subset of) other shared objects
    without exposing those symbols to the entire application.
    This can be achieved by using a separate namespace and the
    RTLD_GLOBAL

    flag.

    The
    dlmopen()

    function also can be used to provide better isolation than the
    RTLD_LOCAL

    flag.
    In particular, shared objects loaded with
    RTLD_LOCAL

    may be promoted to
    RTLD_GLOBAL

    if they are dependencies of another shared object loaded with
    RTLD_GLOBAL.

    Thus,
    RTLD_LOCAL

    is insufficient to isolate a loaded shared object except in the (uncommon)
    case where one has explicit control over all shared object dependencies.

    Possible uses of
    dlmopen()

    are plugins where the author of the plugin-loading framework
    can’t trust the plugin authors and does not wish
    any undefined symbols from the plugin framework to be resolved to plugin
    symbols.
    Another use is to load the same object more than once.
    Without the use of
    dlmopen(),

    this would require the creation of distinct copies of the shared object file.
    Using
    dlmopen(),

    this can be achieved by loading the same shared object file into
    different namespaces.

    The glibc implementation supports a maximum of

    16 namespaces.

    Initialization and finalization functions

    Shared objects may export functions using the
    __attribute__((constructor))

    and
    __attribute__((destructor))

    function attributes.
    Constructor functions are executed before
    dlopen()

    returns, and destructor functions are executed before
    dlclose()

    returns.
    A shared object may export multiple constructors and destructors,
    and priorities can be associated with each function
    to determine the order in which they are executed.
    See the
    gcc

    info pages (under «Function attributes»)

    for further information.

    An older method of (partially) achieving the same result is via the use of
    two special symbols recognized by the linker:
    _init

    and
    _fini.

    If a dynamically loaded shared object exports a routine named
    _init(),

    then that code is executed after loading a shared object, before
    dlopen()

    returns.
    If the shared object exports a routine named
    _fini(),

    then that routine is called just before the object is unloaded.
    In this case, one must avoid linking against the system startup files,
    which contain default versions of these files;
    this can be done by using the
    gcc(1)

    -nostartfiles

    command-line option.

    Use of
    _init

    and
    _fini

    is now deprecated in favor of the aforementioned
    constructors and destructors,
    which among other advantages,
    permit multiple initialization and finalization functions to be defined.

    Since glibc 2.2.3,
    atexit(3)

    can be used to register an exit handler that is automatically
    called when a shared object is unloaded.

    History

    These functions are part of the dlopen API, derived from SunOS.

    BUGS

    As at glibc 2.24, specifying the
    RTLD_GLOBAL

    flag when calling
    dlmopen()

    generates an error.
    Furthermore, specifying
    RTLD_GLOBAL

    when calling
    dlopen()

    results in a program crash
    (SIGSEGV)

    if the call is made from any object loaded in a
    namespace other than the initial namespace.

    EXAMPLE

    The program below loads the (glibc) math library,
    looks up the address of the
    cos(3)

    function, and prints the cosine of 2.0.
    The following is an example of building and running the program:

    $ cc dlopen_demo.c -ldl
    $ ./a.out
    -0.416147

    Program source

    #include <stdio.h>
    #include <stdlib.h>
    #include <dlfcn.h>
    #include <gnu/lib-names.h> /* Defines LIBM_SO (which will be a

                                   string such as «libm.so.6») */
    int
    main(void)
    {

        void *handle;

        double (*cosine)(double);

        char *error;


        handle = dlopen(LIBM_SO, RTLD_LAZY);

        if (!handle) {

            fprintf(stderr, «%sn», dlerror());

            exit(EXIT_FAILURE);

        }


        dlerror();    /* Clear any existing error */


        cosine = (double (*)(double)) dlsym(handle, «cos»);


        /* According to the ISO C standard, casting between function

           pointers and ‘void *’, as done above, produces undefined results.

           POSIX.1-2003 and POSIX.1-2008 accepted this state of affairs and

           proposed the following workaround:


               *(void **) (&cosine) = dlsym(handle, «cos»);


           This (clumsy) cast conforms with the ISO C standard and will

           avoid any compiler warnings.


           The 2013 Technical Corrigendum to POSIX.1-2008 (a.k.a.

           POSIX.1-2013) improved matters by requiring that conforming

           implementations support casting ‘void *’ to a function pointer.

           Nevertheless, some compilers (e.g., gcc with the ‘-pedantic’

           option) may complain about the cast used in this program. */


        error = dlerror();

        if (error != NULL) {

            fprintf(stderr, «%sn», error);

            exit(EXIT_FAILURE);

        }


        printf(«%fn», (*cosine)(2.0));

        dlclose(handle);

        exit(EXIT_SUCCESS);
    }

    COLOPHON

    This page is part of release 5.05 of the Linux
    man-pages

    project.
    A description of the project,
    information about reporting bugs,
    and the latest version of this page,
    can be found at
    https://www.kernel.org/doc/man-pages/.

    SEE ALSO

    ld(1),

    ldd(1),

    pldd(1),

    dl_iterate_phdr(3),

    dladdr(3),

    dlerror(3),

    dlinfo(3),

    dlsym(3),

    rtld-audit(7),

    ld.so(8),

    ldconfig(8)

    gcc info pages, ld info pages

    Понравилась статья? Поделить с друзьями:
  • Dllkernel32 dll ошибка
  • Dllhost exe ошибка приложения
  • Dllhost exe error
  • Dll программа для устранения ошибки
  • Dll xrcdb dll как исправить ошибку