2015-06-08 7 views
8

Sto cercando di elaborare l'output di un nm o di read-s su un file eseguibile. Tuttavia, ho difficoltà a distinguere le funzioni statiche l'una dall'altra nell'output.Come differenziare le funzioni statiche con l'output nm o readelf in C

Ecco quello che sto lavorando con:

test.c

static int foo() { 
    int x = 6; 
} 

main() {} 

other.c

static int foo() { 
    int x = 5; 
} 

compilo questi in questo modo:

gcc -o test test.c other.c 

e quindi eseguire un comando nm per ottenere tutti i simboli:

nm test 

Tra i quali i seguenti due simboli (per le mie funzioni statiche) compaiono:

00000000004004ed t foo 
0000000000400500 t foo 

Esiste un metodo per essere in grado per distinguere da quale file appariva la funzione foo specifica? O avrò bisogno di fare un po 'di magia prima di compilarlo per farlo funzionare?

Dovrei aggiungere che per il mio caso d'uso, ho accesso al file binario finale e ai file oggetto utilizzati da esso, ma non posso effettivamente realizzarlo da solo per assicurarmi che abbia una tabella dei simboli.

Grazie!

+1

per quanto ne so, le informazioni sul file di origine sono memorizzate in informazioni di debug. Il binario completamente spogliato non avrà tali informazioni. Per leggere le informazioni di debug, consulta l'opzione 'readelf' --debug-dump. Simili - debug-syms per 'nm' –

+0

Compila con' -g' e usa 'nm -l' – 4566976

+0

Dovrei aggiungere che per il mio caso d'uso, ho accesso al binario finale e ai file oggetto usati da esso, ma Non riesco a creare o garantire le informazioni di debug nel binario. – Andrew

risposta

9

tua domanda presuppone che, dato un file eseguibile, è sempre possibile scoprire i nomi delle funzioni static (locali) che sono stati compilati in esso, utilizzando nm o un altro strumento. Così sarai in grado di vedere quando due o altri nomi sono gli stessi e sollevare la questione su come scoprire da quali file sorgente sono stati compilati.

Tuttavia, tale ipotesi è falsa. Nel caso di gcc, se i file sono compilati con con ottimizzazione -O0, nella tabella di simboli del file dell'oggetto verranno emessi i simboli locali. -O0 è l'impostazione predefinita, in modo che si applica nel caso di vostro:

gcc -o test test.c other.c 

Ma se i file vengono compilati ad ogni livello di ottimizzazione maggiore - come certamente saranno per una build di rilascio - allora i simboli locali vengono omessi dalla oggetto tabella dei simboli dei file. Quindi il linker non li vede mai. Quindi non è possibile recuperare dal file eseguibile con nm o qualsiasi altra cosa.

compilare il file di esempio con:

gcc -O1 -o test test.c other.c 

poi nm test di nuovo, e noterete che il:

00000000004004ed t foo 
0000000000400500 t foo 

sono scomparsi, insieme a tutti gli altri nomi di funzione statica.

In tal caso, se come dici non è possibile controllare come l'eseguibile è costruito, allora non si può garantire che si tratta di anche possibile per la tua domanda a sorgere.

Se potessi di controllo come l'eseguibile è costruito per garantire che i file siano compilato con -O0, poi ci sono diversi modi in cui è possibile legare i nomi delle funzioni statiche per i file di origine.Due quelli altrettanto semplice sarebbe:

readelf -s test 

e

objdump -t test 

ognuna delle quali presenterà l'elenco un nome di file sorgente alla testa di ciascun pezzo di simboli che ne derivano.

(E se ha bisogno dire, l'approccio suggerito da gdb da @Amol non sfugge la restrizione che l'eseguibile deve essere compilato con l'ottimizzazione -O0)

2

Potrebbe essere necessario leggere la tabella dei simboli ELF ed estrarre il valore ELF32_ST_BIND.

Secondo la specifica ELF (vedi http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf) i valori per ELF32_ST_BIND possono essere:.

 Name   Value 
    STB_LOCAL  0 
    STB_GLOBAL  1 
    STB_WEAK  2 
    STB_LOPROC 13 
    STB_HIPROC 15 

Dove STB_LOCAL è definito come "simboli locali non sono visibili al di fuori del file oggetto che contiene la loro definizione simboli locali di lo stesso nome può esistere in più file senza interferire l'uno con l'altro. " che sembrano corrispondere abbastanza bene con le funzioni C statiche.

Ad esempio, prendendo di campionare e modificando leggermente:

test.c: 

    static int foo() { 
     int x = 5; 
    } 

    int bar() 
    { 
     int y = 6; 
    } 

    main() {} 

    other.c: 

    static int foo() 
    { 
     int x = 7; 
    } 

e la compilazione con gcc -o test test.c other.c e guardando la tabella dei simboli (un sacco di voci rimosse):

readelf -s test 
    Num: Value   Size Type Bind Vis  Ndx Name 
    37: 00000000004004f0 13 FUNC LOCAL DEFAULT 13 foo 
    39: 0000000000400510 13 FUNC LOCAL DEFAULT 13 foo 
    52: 00000000004004fd 13 FUNC GLOBAL DEFAULT 13 bar 

Possiamo vedere che le due funzioni statiche si presentano come LOCAL e la funzione "normale" mostra un GLOBAL

Nota: mentre questo metodo funziona con file non di debug, se Il file finale è stato rimosso, non possiamo usare questo metodo.

3

Ho provato la seguente sequenza.

Se il file di output è stato rimosso senza simboli di debug, utilizzare gdb è possibile creare il file oggetto. Usare i comandi, come di seguito:

$ gdb a.out 

darà seguito come output

Reading symbols from /home/amol/amol/a.out...(no debugging symbols found)...done. 

Poi (gdb) verranno nel terminale

Dare i seguenti comandi in sequenza ((gdb) pronta vengono di default, come si continua a digitare i comandi)

(gdb) maint print symbols filename 
(gdb) maint print psymbols filename 
(gdb) maint print msymbols filename 

Ora nella cartella è possibile visualizzare un file con nome nome file. Aprire il file in editor di testo allora si può vedere le informazioni come di seguito:

[ 8] t 0x80483b4 foo section .text test.c 
[ 9] T 0x80483c3 main section .text other.c 
[10] t 0x80483c8 foo section .text other.c 

Qui si può vedere chiaramente ciò che foo() funzione è venuto da quale file .c. Spero che questo ti aiuti.

+0

Grazie per la risposta. Probabilmente avrei dovuto dire che voglio che il mio metodo prenda qualsiasi eseguibile arbitrario, quindi una tabella dei simboli potrebbe anche essere omessa e il metodo dovrebbe ancora funzionare. Tuttavia, dopo aver visto la tua risposta e molti altri, non penso sia possibile. Grazie ancora! – Andrew

0

Se dovesse funzionare su qualsiasi eseguibile spogliato si potrebbe impiantare una stringa nelle funzioni e cercarle nell'eseguibile.

#define STR1(x) #x 
#define STR(x) STR1(x) 
#define FUNCID(funcname) __asm__ __volatile__ (\ 
    "jmp 1f;"\ 
    ".string \"" __FILE__ "/" STR(funcname) "()\";"\ 
    "1:"\ 
) 

static int foo() { 
    FUNCID(foo); 
    return rand(); 
} 
Problemi correlati