2015-02-16 24 views
6

Sto tentando di creare un hook sulla funzione di sistema open(). L'ho fatto seguendo le linee seguenti.Errore di segmentazione dal gancio aperto()

Ho creato una libreria wrapper con il seguente:

extern int mocked_open(const char* fn, int flags, va_list args); 

int open(const char* fn, int flags, ...) 
{ 
    int r = -1; 
    va_list args; 

    va_start(args, flags); 
    r = mocked_open(fn, flags, args); 
    va_end(args); 

    return r; 
} 

compilo questo in libwrapper.so, che mi carico con LD_PRELOAD.

L'attuazione di mocked_open() è la seguente (io uso il quadro CPPUtest):

int mocked_open(const char* fn, int flags, va_list args) 
{ 
    if (strncmp(fn, test_device_id, 11) == 0) 
    { 
     return mock().actualCall("open").returnValue().getIntValue(); 
    } 
    else 
    { 
     int r = -1; 
     int (*my_open)(const char*, int, ...); 
     void* fptr = dlsym(RTLD_NEXT, "open"); 
     memcpy(&my_open, &fptr, sizeof(my_open)); 

     if (flags & O_CREAT) 
     { 
      r = my_open(fn, flags, va_arg(args, mode_t)); 
     } 
     else 
     { 
      r = my_open(fn, flags); 
     } 

     return r; 
    } 
} 

Il test_device_id è una semplice stringa ("test_device"), che spero non viene utilizzato altrove.

Durante l'esecuzione dei test, l'eseguibile si arresta in modo anomalo con un errore di segmentazione. Ho tracciato questo verso la funzionalità di profilazione GCC, che vuole aprire/creare un gruppo di file .gcda e chiama open() per questo.

Dopo un po 'di debug con strace (per suggerimento sotto), ho trovato che la riga r = my_open(fn, flags, va_arg(args, mode_t)); è effettivamente il colpevole. Viene chiamato ricorsivamente, o così sembra: vedo molte chiamate a questa linea, senza che la funzione ritorni. Quindi un segfault. Il file che si sta aprendo è il corrispondente file .gcda (per la profilazione). In effetti, il segfault si verifica solo con il profilo abilitato.

+0

Penso che il tuo problema sia la gestione variadica http://stackoverflow.com/questions/150543/forward-an-invocation-of-a-variadic-function-in-c – user590028

+0

@ user590028: puoi elaborare? Ho pensato che ci fosse qualcosa di sbagliato nella gestione degli argomenti variadici (come ho scritto), ma cosa ...? – Ludo

+1

Che tipo di chiamate a 'open()' fa 'strace' show è usato nei tuoi casi di test? – alk

risposta

4

Quando si compila con gcov profiling abilitato, il compilatore inserisce codice aggiuntivo nelle vostre funzioni, per tenere traccia di quali è stato eseguito il codice. In pseudocodice ruvida, che inserito il codice farà (tra le altre cose):

if (!output_file_has_been_opened) { 
    fd = open(output_filename, ...); 
    check_ok(fd); 
    output_file_has_been_opened = TRUE; 
    track_coverage(); 
} 

... quindi se il file di output non è ancora stato aperto con successo (come all'inizio del programma), tenterà per aprirlo. Sfortunatamente, in questo caso chiameremo la tua funzione simulata open(), che ha lo stesso codice inserito; dal momento che il file non è stato ancora aperto correttamente e poiché il codice gcov non è consapevole dell'esistenza di qualcosa di insolito, tenterà nuovamente la chiamata open() - e questo è ciò che sta causando la ricorsione (e l'eventuale segfault, una volta che lo stack è esausto).

+0

Hmm, penso che questo la spiegazione deve essere alquanto incompleta, perché in genere gcov tenterà solo di aprire il file quando scrive il suo output, ad esempio quando un programma si chiude, ma anche in altre occasioni. Qualcosa deve scatenarlo per scrivere il file .gcda (quindi perché stai vedendo che tutto sta accadendo affatto), ma non mi è chiaro adesso cosa sia ... – psmears

+0

Questa spiegazione mi sembra estremamente plausibile ed è coerente con le mie osservazioni. Vedo le chiamate a 'apri' per aprire il file gcda _after_ i miei test sono completati. Non sono sicuro di quanto codice di rimozione abbia CPPUTest, ma per quanto posso dire, le chiamate sono praticamente alla fine dell'esecuzione del programma. La tua spiegazione tuttavia implica anche che non riesco a mettere un aggancio su 'open()' con la creazione dei profili abilitata ... – Ludo

+0

@Ludo: Grazie! Ad essere sincero, sento ancora che manca un pezzo del puzzle: non mi è chiaro che cosa stia causando la roba di gcov a scrivere il suo file. Se riusciamo a capirlo, potrebbe esserci un modo per mantenere l'hook 'open()' con il profiling abilitato. Gdb fornisce una traccia di stack significativa per l'errore di segmentazione? In ogni caso, una soluzione potrebbe essere (se il tuo sistema di compilazione lo consente) compilare la libreria LD_PRELOAD senza profilare, ma il resto del codice con - non otterrai i dati di gcov per la libreria di test, ma presumibilmente questo è meno importante rispetto al codice in prova? – psmears

5

Prova questa

typedef int (*OpenFunction)(const char* fn, int flags, ...); 

poi

OpenFunction function; 
void  **pointer; 

pointer = (void **)&function; 
*pointer = dlsym(RTLD_NEXT, "open"); 

questo è un esempio di lavoro completo

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <dlfcn.h> 
#include <unistd.h> 
#include <fcntl.h> 

#include <errno.h> 

typedef int (*OpenFunction)(const char* fn, int flags, ...); 

int main(int argc, char **argv) 
{ 
    OpenFunction function; 
    void  *dl; 
    int   fd; 
    void  **pointer; 

    if (argc < 2) 
     return -1; 
    pointer = (void **)&function; 
    *pointer = dlsym(RTLD_NEXT, "open"); 

    fd = function(argv[1], O_RDONLY); 
    if (fd != -1) 
    { 
     printf("file opened succesfully\n"); 
     close(fd); 
    } 
    else 
    { 
     printf("%s: cannot open the file\n", strerror(errno)); 
    } 
    return 0; 
} 
+0

Ho letto qui (http://stackoverflow.com/questions/10519312/how-does-this-make-sense-void-fptr-dlsymhandle-my-function) che l'utilizzo di questo tipo di cast non era particolarmente raccomandato. – Ludo

+1

Per la cronologia: l'utilizzo dell'approccio per ottenere il puntatore della funzione non fa la differenza, ottengo ancora l'errore di segmentazione. – Ludo

+0

@Ludo L'ho aggiornato nel caso non ti piacesse il cast, e poiché stai ancora ricevendo l'errore di segmentazione, il problema è un altro problema, perché l'esempio funziona correttamente. –