2009-08-30 8 views
5

que estoy tratando de hacer algo como lo siguientedlsym/dlopen con argumentos de ejecución

enum types {None, Bool, Short, Char, Integer, Double, Long, Ptr}; 
    int main(int argc, char ** args) { 
    enum types params[10] = {0}; 
    void* triangle = dlopen("./foo.so", RTLD_LAZY); 
    void * fun = dlsym(triangle, ars[1]); 

    <<pseudo code>> 
    } 

Dónde pseudo código es algo así como

fun = {} 
for param in params: 
     if param == None: 
     fun += void 
     if param == Bool: 
      fun += Boolean 
     if param == Integer: 
      fun += int 
     ... 
returnVal = fun.pop() 
funSignature = returnval + " " + funName + "(" + Riffle(fun, ",") + ")" 
exec funSignature 

Gracias

+0

> Estoy tratando de hacer algo como lo siguiente Entonces, ¿qué ocurre cuando pruebas esto? – a2800276

+0

@ a2800276: el compilador se queja de una multitud de problemas de sintaxis. El problema más profundo es malinterpretar el servicio proporcionado por 'dlopen()' y 'dlsym()', etc. –

Respuesta

19

En realidad, puede hacer casi todo lo que desee. En el lenguaje C (a diferencia de C++, por ejemplo), las funciones en los objetos compartidos se referencian simplemente por sus nombres. Por lo tanto, para encontrar, y lo que es más importante, para llame al --la función adecuada, no necesita su firma completa. ¡Solo necesitas su nombre! Es una ventaja y una desventaja, pero esa es la naturaleza del idioma que eliges.

Déjame demostrar cómo funciona.

#include <dlfcn.h> 

typedef void* (*arbitrary)(); 
// do not mix this with typedef void* (*arbitrary)(void); !!! 

int main() 
{ 
    arbitrary my_function; 
    // Introduce already loaded functions to runtime linker's space 
    void* handle = dlopen(0,RTLD_NOW|RTLD_GLOBAL); 
    // Load the function to our pointer, which doesn't know how many arguments there sould be 
    *(void**)(&my_function) = dlsym(handle,"something"); 
    // Call something via my_function 
    (void) my_function("I accept a string and an integer!\n",(int)(2*2)); 
    return 0; 
} 

De hecho, puede llamar a cualquier función de esa manera. Sin embargo, hay un inconveniente. De hecho, necesita saber el tipo de retorno de su función en tiempo de compilación. De forma predeterminada, si omite void * en ese typedef, se asume que int es un tipo de retorno, y sí, es un código C correcto. El problema es que el compilador necesita saber el tamaño del tipo de devolución para operar la pila correctamente.

Puede solucionarlo mediante trucos, por ejemplo, precribiendo previamente varios tipos de funciones con diferentes tamaños de tipos de devolución y luego seleccionando a cuál va a llamar en realidad.Pero la solución más fácil es requerir funciones en su complemento para devolver void * o int always; el resultado real se devuelve mediante punteros dados como argumentos.

Lo que debe asegurarse es que siempre llame a la función con el número exacto y los tipos de argumentos que se supone que debe aceptar. Preste más atención a la diferencia entre diferentes tipos de enteros (su mejor opción sería emitirles argumentos explícitamente).

Varios comentadores informaron que no se garantiza que el código anterior funcione para funciones variadas (como printf).

+1

@pavel: ¿Podría hacer algo como tipo de unión { int i; doble d; flotador f; } tipo; (tipo) my_printf (...)? – adk

+0

@adk: No veo nada malo con los sindicatos. De hecho, me olvidé completamente de ellos, ¡así que gracias por mejorar mi respuesta! :) –

+0

Pavel: 'printf' es un mal ejemplo, porque la declaración de lista de parámetros vacía no es compatible con las funciones varargs. (Y el tipo de retorno de 'printf' es' int', por cierto, no 'void *'). – caf

17

¿Qué dlsym() retornos es normalmente una puntero a la función - disfrazado como void *. (Si usted pregunta por el nombre de una variable global, que le devuelve un puntero a la variable global, también.)

A continuación, invocar esa función al igual que es posible utilizar cualquier otro puntero a funcionar:

int (*fun)(int, char *) = (int (*)(int, char *))dlsym(triangle, "function"); 

(*fun)(1, "abc"); # Old school - pre-C89 standard, but explicit 
fun(1, "abc");  # New school - C89/C99 standard, but implicit 

Soy de la vieja escuela; Prefiero la notación explícita para que el lector sepa que 'diversión' es un puntero a una función sin necesidad de ver su declaración. Con la nueva notación escolar, debe recordar buscar una variable 'fun' antes de intentar encontrar una función llamada 'fun()'.

Tenga en cuenta que no puede crear la llamada de función de forma dinámica como lo está haciendo - o, en general. Hacer eso requiere mucho más trabajo. Debe saber con anticipación qué espera el puntero de la función en cuanto a los argumentos y qué devuelve y cómo interpretarlo todo.

Los sistemas que administran llamadas a funciones más dinámicas, como Perl, tienen reglas especiales sobre cómo se llaman las funciones y se pasan los argumentos y no llaman (posiblemente no pueden llamar) funciones con firmas arbitrarias. Solo pueden llamar a funciones con firmas que se conocen de antemano. Un mecanismo (no usado por Perl) es insertar los argumentos en una pila y luego llamar a una función que sepa cómo recolectar valores de la pila. Pero incluso si esa función llamada manipula esos valores y luego llama a una otra función arbitraria, esa función llamada proporciona la secuencia de llamada correcta para la otra función arbitraria.

La reflexión en C es difícil, muy difícil. No se puede deshacer, pero requiere infraestructura para admitirlo y disciplina para usarlo, y solo puede llamar a funciones que respaldan las reglas de la infraestructura.

+0

Sé cuáles son los argumentos por adelantado y los tipos de devolución, mi pregunta es si puedo crear los moldes dinámicamente. Gracias – adk

+1

No; no puedes crear los moldes dinámicamente. C es un lenguaje compilado; lo que pretendes hacer es interpretar el código C, ¡que no es una proposición trivial! –

+0

gracias. Esto es lo que quería saber – adk

Cuestiones relacionadas