sirven que punteros puntero para los funciones estructura ejemplo dev datos con arreglos aritmetica apuntadores c pointers dlopen dlsym

que - ¿Cómo asignar correctamente un puntero devuelto por dlsym en una variable de tipo de puntero de función?



punteros y arreglos en c (6)

Estoy tratando de usar dlopen() y dlsym() en mi código y compilarlo con gcc .

Aquí está el primer archivo.

/* main.c */ #include <dlfcn.h> int main() { void *handle = dlopen("./foo.so", RTLD_NOW); if (handle) { void (*func)() = dlsym(handle, "func"); func(); } return 0; }

Aquí está el segundo archivo.

/* foo.c */ #include <stdio.h> void func() { printf("hello, world/n"); }

Así es como compilo y ejecuto el código.

$ 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

¿Cómo puedo deshacerme de la advertencia?

Tipo de fundición no ayuda. Si intento escribir el valor de retorno de dlsym() en un puntero de función, recibo esta advertencia en su lugar.

main.c:10:26: warning: ISO C forbids conversion of object pointer to function pointer type [-Wpedantic] void (*func)() = (void (*)()) dlsym(handle, "func"); ^

¿Qué convencería al compilador de que este código está bien?


El compilador solo "intenta ayudar", por lo que tiene que usar dos tipografías:

#include <inttypes.h> void (*func)() = (void (*)())(intptr_t)dlsym(handle, "func");


El problema aquí es que un puntero a objeto está separado sutilmente de un puntero de función. En ISO / IEC 9899: artículo 201x §6.3.2.3 Punteros se indica:

  1. Un puntero para anular se puede convertir ao desde un puntero a cualquier tipo de objeto. Un puntero a cualquier tipo de objeto se puede convertir en un puntero para anularlo y viceversa; El resultado se comparará igual al puntero original.

.

  1. Un puntero a una función de un tipo puede convertirse en un puntero a una función de otro tipo y viceversa; El resultado se comparará igual al puntero original. Si se usa un puntero convertido para llamar a una función cuyo tipo no es compatible con el tipo apuntado a, el comportamiento es indefinido.

Por lo tanto, un puntero de función es diferente de los punteros de objeto y, en consecuencia, la asignación de un void * a un puntero de función siempre es estrictamente no compatible .

De todos modos, como dije en los comentarios, en 99.9999 ... 9999% de los casos está permitido gracias al ANEXO J - Problemas de portabilidad, §J.5.7 Reparto de punteros de función del documento mencionado anteriormente que dice:

  1. Un puntero a un objeto o al vacío se puede convertir en un puntero a una función, lo que permite invocar datos como una función (6.5.4).
  2. Un puntero a una función se puede convertir en un puntero a un objeto o anularlo, lo que permite que una función sea inspeccionada o modificada (por ejemplo, por un depurador) (6.5.4).

Ahora en el lado práctico, una técnica que evita la división de código en más archivos es usar pragmas para suprimir las advertencias pedantes de un pequeño fragmento de código.
La forma más brutal puede ser:

/* 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

Se podría actuar de una manera más sofisticada aislando el código infractor en una función pequeña, y luego forzando su alineación. Es más un maquillaje que una solución efectiva, pero suprimirá el diagnóstico no deseado:

/* 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; }



Para mantener la opción -pedantic para su código mientras tiene partes de código que no son estrictamente conformes, separe ese código en un archivo separado con opciones de advertencia personalizadas.

Por lo tanto, dlsym una función que dlsym función dlsym y devuelva un puntero a la función. Póngalo en un archivo separado y compile ese archivo sin -pedantic .


Puedes usar union , asi:

union { void *ptr; void (*init_google_logging) (char* argv0); } orig_func; orig_func.ptr = dlsym (RTLD_NEXT, "_ZN6google17InitGoogleLoggingEPKc"); orig_func.init_google_logging (argv0);


Si desea ser pedanticalmente correcto, no intente resolver la dirección de una función. En su lugar, exporte algún tipo de estructura desde la biblioteca dinámica:

En la biblioteca

struct export_vtable { void (*helloworld)(void); }; struct export_vtable exports = { func };

En la persona que llama

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; }

Esta técnica es bastante común, no para la portabilidad (POSIX garantiza que los punteros de función se pueden convertir hacia y desde el vacío *), pero porque permite una mayor flexibilidad.