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

42.1.2. Анализ ошибок: dlerror()

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

#include

const char *dlerror(void);

Возвращает указатель на строку с описанием ошибки или NULL, если с момента последнего вызова dlerror() никаких ошибок не было

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

42.1.3. Получение адреса символа: dlsym()

Функция dlsym() ищет именованный символ (symbol — функцию или переменную) в библиотеке, на которую указывает дескриптор (handle), и в ее дереве зависимостей.

#include

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

Возвращает адрес символа или NULL, если символ не был найден

Если символ найден, dlsym() возвращает его адрес; в противном случае возвращается NULL. В качестве аргумента handle обычно выступает дескриптор библиотеки, возвращенный предыдущим вызовом dlopen(). Но это может быть и один из так называемых псевдодескрипторов, описанных ниже.

У dlsym() есть родственная функция, dlvsym(handle, symbol, version), которая имеет похожее назначение, но может применяться для поиска в библиотеке версионных символов, чьи версии совпадают со значением аргумента version (версионирование символов будет описано в подразделе 42.3.2). Для получния объявления этой функции из заголовочного файла нужно определить макрос проверки возможностей _GNU_SOURCE.

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

При наличии в аргументе symbol имени переменной можно присвоить результат, возвращенный dlsym(), указателю соответствующего типа и, разыменовав его, извлечь значение этой переменной:

int *ip;

ip = (int *) dlsym(symbol, "myvar");

if (ip!= NULL)

printf("Value is %d\n", *ip);

Если аргумент symbol содержит имя функции, то эту функцию можно вызвать с помощью указателя, полученного в результате вызова dlsym(). Результат выполнения dlsym() можно поместить в указатель подходящего типа, как показано ниже:

int (*funcp)(int); /* Указатель на функцию, принимающую целочисленный

аргумент и возвращающую целочисленный результат */

Однако мы не можем просто присвоить результат выполнения dlsym() такому указателю, как показано ниже:

funcp = dlsym(handle, symbol);

Причина в том, что стандарт C99 запрещает операцию присваивания между указателем на функцию и void *. В качестве решения можно воспользоваться (немного грубым) приведением типов:

*(void **) (&funcp) = dlsym(handle, symbol);

Получив указатель на функцию с помощью dlsym(), можно вызвать ее путем обычной для языка C операции разыменовывания:

res = (*funcp)(somearg);

Вместо синтаксиса вида *(void **), приведенного выше, для присваивания значения, возвращенного функцией dlsym(), можно воспользоваться почти равнозначной альтернативой:

(void *) funcp = dlsym(handle, symbol);

Но если при компиляции задействовать параметр gcc — pedantic, то для этого кода будет выдано предупреждение ANSI C forbids the use of cast expressions as lvalues (Стандарт ANSI C запрещает использование приведения типов в качестве значений lvalue). Выражение *(void **) будет интерпретировано без замечаний, поскольку оно присваивает данные по адресу, на который указывает значение lvalue.

Во многих реализациях UNIX можно избавиться от предупреждений компилятора, применив следующее приведение типов:

funcp = (int (*) (int)) dlsym(handle, symbol);

Однако в первой технической поправке к стандарту SUSv3 (Technical Corrigendum Number 1, TC1), касающейся функции dlsym(), отмечается, что стандарт C99 требует, чтобы для подобных преобразований компиляторы выводили предупреждение, и предлагается использовать вместо этого синтаксис *(void **), описанный выше.

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

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