В поправке 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-системах. Чтобы получить определение этих констант из заголовочного файла
Пример программы
Применение программного интерфейса 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 *
Возвращает 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 *