Как правильно назначить указатель, возвращаемый dlsym, переменной типа указателя функции?
Я пытаюсь использовать dlopen()
и dlsym()
в моем коде и скомпилируйте его с помощью gcc
.
вот первый файл.
/* main.c */
#include <dlfcn.h>
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
вот второй файл.
/* foo.c */
#include <stdio.h>
void func()
{
printf("hello, worldn");
}
вот как я компилирую и запускаю код.
$ gcc -std=c99 -pedantic -Wall -Wextra -shared -fPIC -o foo.so foo.c
$ gcc -std=c99 -pedantic -Wall -Wextra -ldl -o main main.c
main.c: In function ‘main’:
main.c:10:26: warning: ISO C forbids initialization between function pointer and ‘void *’ [-Wpedantic]
void (*func)() = dlsym(handle, "func");
^
$ ./main
hello, world
как я могу избавиться от предупреждения?
тип литья не поможет. Если я попытаюсь ввести cast возвращаемое значение dlsym()
в указатель функции, я получаю это предупреждение вместо этого.
main.c:10:26: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic]
void (*func)() = (void (*)()) dlsym(handle, "func");
^
что убедит ли компилятор, что этот код в порядке?
5 ответов
Если вы хотите быть педантически корректным, не пытайтесь разрешить адрес функции. Вместо этого экспортируйте некоторую структуру из динамической библиотеки:
в библиотеке
struct export_vtable {
void (*helloworld)(void);
};
struct export_vtable exports = { func };
на абонента
struct export_vtable {
void (*helloworld)(void);
};
int main() {
struct export_vtable* imports;
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
imports = dlsym(handle, "exports");
if (imports) imports->helloworld();
}
return 0;
}
этот метод на самом деле довольно распространен, не для переносимости-POSIX гарантирует, что указатели функций могут быть преобразованы в void* и из void*, но потому, что он обеспечивает большую гибкость.
проблема здесь в том, что указатель на объект тонко отделен от указателя функции. В ISO / IEC 9899: 201x статьи §6.3.2.3 указатели указано:
- указатель на Void может быть преобразован в указатель на любой тип объекта. Указатель на любой тип объекта может быть преобразован в указатель на void и обратно; результат должен быть равен оригинальный указатель.
.
- указатель на функцию одного типа может быть преобразован в указатель на функция другого типа и обратно; результат должен сравниваться равен исходному указателю. Если преобразованный указатель используется для вызовите функцию, тип которой не совместим с указанным тип, поведение не определено.
таким образом, указатель функции отличается от указателей объектов, и, следовательно, задание void *
для указателя функции всегда строго не соответствует.
в любом случае, как я сказал в комментарии, в 99.9999....9999% случаев это разрешено благодаря приложение J-вопросы переносимости, §J. 5.7 функция наведения указателя вышеупомянутого документа, в котором говорится:
- указатель на объект или на void может быть приведен к указателю на функция, позволяющая вызывать данные как функцию (6.5.4).
- указатель на функцию может быть приведен к указателю на объект или void, позволяющая проверять или изменять функцию (например, отладчиком) (6.5.4).
Теперь на практической стороне техника, которая избегает разделения кода в большем количестве файлов, заключается в использовании прагм для подавления педантичных предупреждений для небольшого фрагмента кода.
Более брутальной формой может быть:
/* main.c */
#include <dlfcn.h>
#pragma GCC diagnostic push //Save actual diagnostics state
#pragma GCC diagnostic ignored "-pedantic" //Disable pedantic
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = dlsym(handle, "func");
func();
}
return 0;
}
#pragma GCC diagnostic pop //Restore diagnostics state
более изощренный путь смог быть сработало изолирование кода-нарушителя в небольшой функции, затем принудительное его встраивание. Это больше макияж, чем эффективное решение, но будет подавлять нежелательную диагностику:
/* main.c */
#include <dlfcn.h>
#pragma GCC diagnostic push //Save actual diagnostics state
#pragma GCC diagnostic ignored "-pedantic" //Disable pedantic
void (*)() __attribute__((always_inline)) Assigndlsym(void *handle, char *func)
{
return dlsym(handle, func); //The non compliant assignment is done here
}
#pragma GCC diagnostic pop //Restore diagnostics state
int main()
{
void *handle = dlopen("./foo.so", RTLD_NOW);
if (handle) {
void (*func)() = Assigndlsym(handle, "func"); //Now the assignment is compliant
func();
}
return 0;
}
сохранить -pedantic
опция для вашего кода, имея части кода, которые не строго соответствуют, отделите этот код в отдельный файл с пользовательскими параметрами предупреждения.
Итак, сделайте функцию, которая обертывает dlsym
function и возвращает указатель на функцию. Поместите его в отдельный файл и скомпилируйте этот файл без -pedantic
.
можно использовать union
, например:
union {
void *ptr;
void (*init_google_logging) (char* argv0);
} orig_func;
orig_func.ptr = dlsym (RTLD_NEXT, "_ZN6google17InitGoogleLoggingEPKc");
orig_func.init_google_logging (argv0);
компилятор только "пытается помочь", поэтому вам нужно использовать два типа:
#include <inttypes.h>
void (*func)() = (void (*)())(intptr_t)dlsym(handle, "func");