В поправке SUSv3 TC1 также отмечается, что из-за необходимости применения операции *(void **) будущая версия стандарта может содержать отдельный программный интерфейс, похожий на dlsym() и предназначенный для работы с указателями на данные и функции. Однако в стандарте SUSv4 никаких подобных изменений не предусмотрено.

Использование псевдодескрипторов в сочетании с функцией dlsym()

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

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

• RTLD_NEXT — поиск символа выполняется по библиотекам, загруженным после вызова dlsym(). Это может пригодиться при создании функций-оберток, чьи имена совпадают с именами каких-то других функций, определенных где-либо. Например, можно определить в главной программе собственную версию функции malloc() (возможно, ведущую учет выделяемой памяти), которая будет вызывать одноименный оригинал; для этого она должна получить его адрес с помощью вызова func = dlsym(RTLD_NEXT, "malloc").

Значения псевдодескрипторов, описанные выше, не являются обязательными с точки зрения стандарта SUSv3 (хотя в нем они зарезервированы для будущего использования) и доступны не во всех UNIX-системах. Чтобы получить определение этих констант из заголовочного файла , следует сперва определить макрос проверки возможностей _GNU_SOURCE.

Пример программы

Применение программного интерфейса dlopen продемонстрировано в листинге 42.1. Эта программа принимает два аргумента командной строки: имя разделяемой библиотеки, которую нужно загрузить, и имя функции, которую нужно вызвать из этой библиотеки. Ниже показаны примеры запуска данной программы:

$ ./dynload./libdemo.so.1 x1

Called mod1-x1

$ LD_LIBRARY_PATH=. /dynload libdemo.so.1 x1

Called mod1-x1

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

Листинг 42.1. Использование программного интерфейса dlopen

shlibs/dynload.c

#include

#include "tlpi_hdr.h"

int

main(int argc, char *argv[])

{

void *libHandle; /* Дескриптор для разделяемой библиотеки */

void (*funcp)(void); /* Указатель на функцию без аргументов */

const char *err;

if (argc!= 3 || strcmp(argv[1], "-help") == 0)

usageErr("%s lib-path func-name\n", argv[0]);

/* Загружаем разделяемую библиотеку и получаем дескриптор

для ее дальнейшего использования */

libHandle = dlopen(argv[1], RTLD_LAZY);

if (libHandle == NULL)

fatal("dlopen: %s", dlerror());

/* Ищем в библиотеке символ с именем, заданным в argv[2] */

(void) dlerror(); /* Очищаем ошибки с помощью dlerror() */

*(void **) (&funcp) = dlsym(libHandle, argv[2]);

err = dlerror();

if (err!= NULL)

fatal("dlsym: %s", err);

/* Если адрес, возвращенный dlsym(), не равен NULL, пытаемся вызвать

это значение как функцию, которая не принимает аргументов */

if (funcp == NULL)

printf("%s is NULL\n", argv[2]);

else

(*funcp)();

dlclose(libHandle); /* Закрываем библиотеку */

exit(EXIT_SUCCESS);

}

shlibs/dynload.c

42.1.4. Закрытие разделяемой библиотеки: dlclose()

Функция dlclose() закрывает библиотеку.

include

int dlclose(void *handle);

Возвращает 0 при успешном завершении или -1 при ошибке

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

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

42.1.5. Получение информации о загруженных символах: dladdr()

Возвращает структуру с информацией об адресе, заданном в аргументе addr (который обычно берется из ранее выполненного вызова dlsym()).

#define _GNU_SOURCE

#include

int dladdr(const void *addr, Dl_info *info);

Перейти на страницу:

Похожие книги