В особых случаях можно присвоить 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 *
Возвращает адрес символа или NULL, если символ не был найден
Если символ найден, dlsym() возвращает его адрес; в противном случае возвращается NULL. В качестве аргумента handle обычно выступает дескриптор библиотеки, возвращенный предыдущим вызовом dlopen(). Но это может быть и один из так называемых псевдодескрипторов, описанных ниже.
У dlsym() есть родственная функция, dlvsym(handle, symbol, version), которая имеет похожее назначение, но может применяться для поиска в библиотеке версионных символов, чьи версии совпадают со значением аргумента version (версионирование символов будет описано в подразделе 42.3.2). Для получния объявления этой функции из заголовочного файла
Значение символа, возвращенного функцией 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 **) будет интерпретировано без замечаний, поскольку оно присваивает данные по адресу,
Во многих реализациях UNIX можно избавиться от предупреждений компилятора, применив следующее приведение типов:
funcp = (int (*) (int)) dlsym(handle, symbol);
Однако в первой технической поправке к стандарту SUSv3 (Technical Corrigendum Number 1, TC1), касающейся функции dlsym(), отмечается, что стандарт C99 требует, чтобы для подобных преобразований компиляторы выводили предупреждение, и предлагается использовать вместо этого синтаксис *(void **), описанный выше.