2015-07-20 22 views
5

Sto utilizzando dlsym() in C e ho una domanda se il valore restituito di dlsym() deve essere espressamente trasmesso o se è implicitamente trasmesso correttamente. Ecco la funzione:Trasmissione durante l'utilizzo di dlsym()

double (*(compile)(void))(double x, double y) 
{ 
    if (system("scan-build clang -fPIC -shared -g -Wall -Werror -pedantic " 
      "-std=c11 -O0 -lm foo.c -o foo.so") != 0) { 
     exit(EXIT_FAILURE); 
    } 

    void *handle; 
    handle = dlopen("./foo.so", RTLD_LAZY); 
    if (!handle) { 
     printf("Failed to load foo.so: %s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 

    foo f; 
    f = (foo)dlsym(handle, "foo"); 
    if (!f) { 
     printf("Failed to find symbol foo in foo.so: %s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 
    return f; 
} 

La funzione compile() non prende un valore e restituisce un puntatore a una funzione che prende due double s come input e che restituisce un letto. Quindi ho impostato una chiamata di sistema che compila un oggetto condiviso foo.so. Quindi apro foo.so con dlopen(). Poi dlsym() trova foo in foo.so e restituisce un oggetto di tipo foo che ho definito in un colpo di testa come:

typedef double (*foo)(double, double); 

Devo lanciare dlsym()?

+0

Vorrei sottolineare che ho trovato la pagina man di 'dlsym()' un po 'opaca in questo senso, ma se qualcuno mi può illuminare sulla pagina di manuale che potrebbe anche aiutare. –

+1

"La rettifica tecnica del 2013 al POSIX.1-2008 (alias POSIX.1-2013) ha migliorato le cose richiedendo che le implementazioni conformi supportino il lancio di" void * "su un puntatore di funzione, tuttavia alcuni compilatori (ad esempio, gcc con '- 'opzione pedante') potrebbe lamentarsi del cast usato in questo programma. " Quando eseguo la compilazione senza cast, al momento 'gcc' e' clang' si lamentano. –

+0

a meno che il compilatore non si lamenti di non volerlo trasmettere. Se si lamenta, assicurati di lanciarlo perché non vede alcun danno nel farlo. – Pradheep

risposta

11

Lo standard C viene scritto in modo da assumere che i puntatori a tipi di oggetto diversi, e in particolare i puntatori a funzionare in contrapposizione ai tipi di oggetto, potrebbero avere rappresentazioni differenti. Questo è il motivo per cui, in generale, non vuoi mescolare i puntatori, o se fai un compilatore moderno ti avviserà, e se vuoi silenziare gli avvertimenti in genere usi un cast esplicito.

dlsym, d'altra parte, per la sua stessa esistenza presuppone che tutti i puntatori sono praticamente la stessa, perché si suppone di essere in grado di restituire voi qualsiasi puntatore a qualsiasi tipo di dati - oggetto o funzione - nel vostro file oggetto.

In altre parole, il codice che utilizza dlsym è intrinsecamente non portabile, nel senso che non è ampiamente portatile, nel senso che è portatile "solo" per quelle macchine in cui tutti i puntatori sono interconvertibili in modo sicuro. (Che è, naturalmente, praticamente tutte le macchine popolari oggi.)

Quindi, sì, è necessario lanciare i puntatori per silenziare gli avvertimenti, e così facendo si potrebbe rendere il codice meno portabile alle macchine dove tutti i puntatori non sono uguali (e dove gli avvertimenti, se non controllati, ti informerebbero correttamente che il tuo codice non funzionerà), ma dlsym non funzionerà mai (o nemmeno esisterebbe nella sua forma attuale) su quelle macchine, comunque .

(E se gcc -pedantic si mette in guardia anche un cast esplicito da void * a un tipo di puntatore a funzione, non c'è molto che si può fare tranne che passare a una versione diversa di gcc, o, naturalmente, non utilizzare -pedantic.)


Addendum: La mia risposta ha fatto sembrare che la conversione tra i puntatori a diversi tipi di dati potrebbe essere un problema, ma generalmente non è un problema. Il tipo void * è ben definito per essere il puntatore di dati generico: è ciò che restituisce malloc ed è definito per essere tranquillamente convertibile in qualsiasi tipo di puntatore di oggetto, ovvero, non si suppone che sia necessario un cast. Quindi è quasi una buona scelta per il tipo di ritorno di dlsym, eccetto per il piccolo problema dei puntatori di funzione. malloc non ha mai questo problema (non avresti quasi mai provato a mallocare un puntatore a una funzione), mentre dlsym ha sempre questo problema (i simboli che in genere stai cercando di accedere nei file oggetto caricati dinamicamente sono codice almeno tanto spesso quanto sono dati). Ma i puntatori di funzione non sono garantiti per la conversione da void *, quindi è molto probabile che riceverai avvertimenti, motivo per cui sono necessari i cast e potresti ricevere avvertimenti in -pedantic anche con i cast.

+0

Compilare con' gcc 5.1.0' con '-Wall -Werror -pedantic 'dà un errore:' dynamic_c.c: 49: 6: errore: ISO C vieta la conversione del puntatore dell'oggetto nel tipo di puntatore a funzione [-Werror = pedantic] f = (foo) dlsym (handle, "foo"); '. 'clang 3.6.2' con le stesse bandiere no. –

+1

Lanciare con '* (void **) (& f) = dlsym (handle," foo ");' anche silenzio 'gcc'. –

+1

Sono con Keith: quella contorsione con "void **" sembra una cattiva idea. –

9

dlsym() restituisce un valore void*. Questo valore del puntatore può riferirsi a un oggetto oa una funzione.

Se punta ad un oggetto, quindi non getto è necessario, poiché C definisce una conversione implicita da void* a qualsiasi tipo puntatore a oggetto:

int *ptr = dlsym(handle, "name"); 

Se punta ad una funzione (che è probabilmente molto più comune), non esiste alcuna conversione implicita da void* a qualsiasi tipo di puntatore a funzione, quindi è necessario un cast.

In standard C, non è garantito che un valore void* possa essere convertito in modo significativo in un puntatore di funzione. POSIX, che definisce dlsym(), garantisce implicitamente che il valore void* restituito da dlsym()può essere convertito in modo significativo in un tipo puntatore a funzione, purché il target sia del tipo corretto per la funzione corrispondente.

Supponendo abbiamo a che fare con una funzione di vuoto senza parametri:

typedef void (*func_ptr_t)(void); 
func_ptr_t fptr = (func_ptr_t)dlsym(handle, "name"); 

Si dà il caso, gcc (con -pedantic) mette in guardia su questo:

warning: ISO C forbids conversion of object pointer to function pointer type 

Questo avviso non è strettamente corretto . L'ISO C in realtà non è vietare convertire un puntatore a oggetto in un tipo di puntatore a funzione. Lo standard elenca diversi vincoli che non possono essere violati da un operatore di cast; la conversione di void* in un tipo di puntatore a funzione non è uno di questi. Lo standard C non definisce il comportamento di tale conversione, ma poiché POSIX in questo caso particolare non rappresenta un problema.

Un commento sulla risposta di Steve Summit suggerisce che questo:

*(void **) (&f) = dlsym(handle, "foo"); 

farà tacere gli avvisi di GCC. Lo farà, ma fa supposizioni che non sono garantite da C o POSIX. POSIX garantisce che il risultato di dlsym può essere convertito in un puntatore di funzione; non garantisce che abbia la stessa rappresentazione o allineamento. È probabile che funzioni, ma non lo consiglio.

+1

Ha, strano che la pagina man elenca '* (void **) (& f)' come soluzione POSIX accettabile che "si conforma allo standard ISO C ed eviterà qualsiasi avvertimento del compilatore". Grazie per la precisione aggiuntiva. –

+0

@brauner: interessante. Cita "la Razionale per la specifica POSIX di dlsym()", ma la [specifica POSIX di 'dlsym()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html) ha un sezione Rationale vuota. Forse POSIX ha rimosso questa soluzione alternativa e la pagina man non ha recuperato. –

+0

Probabilmente è stato rimosso per questo motivo: "La rettifica tecnica del 2013 per POSIX.1-2008 (a.k.a POSIX.1-2013) ha migliorato le cose richiedendo che le implementazioni conformi supportino il lancio di" void * "su un puntatore di funzione." Inoltre, la specifica [POSIX di 'dlsym()'] (http://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html) elenca un cast esplicito per un puntatore a funzione in uno dei suoi esempi:/* trova l'indirizzo della funzione mia_funzione */ fptr = (int (*) (int)) dlsym (handle, "my_function"); '. –