2011-12-24 5 views
28

glibc fornisce backtrace() e backtrace_symbols() per ottenere la traccia dello stack di un programma in esecuzione. Ma per farlo funzionare il programma deve essere costruito con il flag -rdynamic del linker.simboli di debug di gcc (-g flag) rispetto all'opzione -dynamic del linker

Qual è la differenza tra il flag -g passato a gcc vs linker -rdynamic flag? Per un codice di esempio mi sono deciso a confrontare le uscite. -rdynamic sembra produrre più informazioni sotto Symbol table '.dynsym' Ma non sono abbastanza sicuro di quali siano le informazioni aggiuntive.

Anche se I strip un file binario del programma creato utilizzando -rdynamic, backtrace_symbols() continua a funzionare.

Quando strip rimuove tutti i simboli dal binario perché lascia dietro a ciò che è stato aggiunto dal flag -rdynamic?

Edit: Follow-up domande basate sulla risposta di Mat seguente ..

Per lo stesso codice di esempio si ha questa è la differenza che vedo con -g & -rdynamic

senza alcuna opzione ..

Symbol table '.dynsym' contains 4 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000000000 218 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
     2: 0000000000000000  0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 
     3: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__ 

    Symbol table '.symtab' contains 70 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000400200  0 SECTION LOCAL DEFAULT 1 
     2: 000000000040021c  0 SECTION LOCAL DEFAULT 2 

con -g ci sono più sezioni, più voci in .symtab tabella ma .dynsym rimane lo stesso ..

 [26] .debug_aranges PROGBITS   0000000000000000 0000095c 
      0000000000000030 0000000000000000   0  0  1 
     [27] .debug_pubnames PROGBITS   0000000000000000 0000098c 
      0000000000000023 0000000000000000   0  0  1 
     [28] .debug_info  PROGBITS   0000000000000000 000009af 
      00000000000000a9 0000000000000000   0  0  1 
     [29] .debug_abbrev  PROGBITS   0000000000000000 00000a58 
      0000000000000047 0000000000000000   0  0  1 
     [30] .debug_line  PROGBITS   0000000000000000 00000a9f 
      0000000000000038 0000000000000000   0  0  1 
     [31] .debug_frame  PROGBITS   0000000000000000 00000ad8 
      0000000000000058 0000000000000000   0  0  8 
     [32] .debug_loc  PROGBITS   0000000000000000 00000b30 
      0000000000000098 0000000000000000   0  0  1 

    Symbol table '.dynsym' contains 4 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000000000 218 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
     2: 0000000000000000  0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 
     3: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__ 

    Symbol table '.symtab' contains 77 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000400200  0 SECTION LOCAL DEFAULT 1 

con -rdynamic senza sezioni supplementari di debug, le voci .symtab sono 70 (lo stesso di vaniglia gcc invocazione), ma più .dynsym voci ..

Symbol table '.dynsym' contains 19 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000000000 218 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
     2: 00000000005008e8  0 OBJECT GLOBAL DEFAULT ABS _DYNAMIC 
     3: 0000000000400750 57 FUNC GLOBAL DEFAULT 12 __libc_csu_fini 
     4: 00000000004005e0  0 FUNC GLOBAL DEFAULT 10 _init 
     5: 0000000000400620  0 FUNC GLOBAL DEFAULT 12 _start 
     6: 00000000004006f0 86 FUNC GLOBAL DEFAULT 12 __libc_csu_init 
     7: 0000000000500ab8  0 NOTYPE GLOBAL DEFAULT ABS __bss_start 
     8: 00000000004006de 16 FUNC GLOBAL DEFAULT 12 main 
     9: 0000000000500aa0  0 NOTYPE WEAK DEFAULT 23 data_start 
     10: 00000000004007c8  0 FUNC GLOBAL DEFAULT 13 _fini 
     11: 00000000004006d8  6 FUNC GLOBAL DEFAULT 12 foo 
     12: 0000000000500ab8  0 NOTYPE GLOBAL DEFAULT ABS _edata 
     13: 0000000000500a80  0 OBJECT GLOBAL DEFAULT ABS _GLOBAL_OFFSET_TABLE_ 
     14: 0000000000500ac0  0 NOTYPE GLOBAL DEFAULT ABS _end 
     15: 00000000004007d8  4 OBJECT GLOBAL DEFAULT 14 _IO_stdin_used 
     16: 0000000000500aa0  0 NOTYPE GLOBAL DEFAULT 23 __data_start 
     17: 0000000000000000  0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 
     18: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__  

    Symbol table '.symtab' contains 70 entries: 
     Num: Value   Size Type Bind Vis  Ndx Name 
     0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
     1: 0000000000400200  0 SECTION LOCAL DEFAULT 1 
     2: 000000000040021c  0 SECTION LOCAL DEFAULT 2 

Ora queste sono le domande che ho ..

  1. In gdb si può fare bt per ottenere il bactrace. Se funziona con solo -g, perché abbiamo bisogno di -rdynamic per il funzionamento di backtrace_symbols?

  2. Confrontando le aggiunte alla .symtab con -g & aggiunte .dynsym con -rdynamic non sono esattamente la stessa cosa .. Ha uno dei due forniscono una migliore informazioni di debug rispetto agli altri? FWIW, la dimensione dell'output prodotto è la seguente: con -g> con -rdynamic> con nessuna opzione

  3. Che cosa è esattamente l'uso di .dynsym? Sono tutti i simboli esportati da questo binario? In tal caso, perché è inutile andare in .dynsym perché non stiamo compilando il codice come una libreria.

  4. Se collego il mio codice utilizzando tutte le librerie statiche, per il backtrace_symbols non è necessario il funzionamento dinamico?

+1

Accade così che backtrace purtroppo non fa uso di simboli di debug [se disponibile] come la maggior parte di altri strumenti, tra cui gdb, valgrind, ecc –

risposta

39

Secondo la documentazione:

Questo indica al linker per aggiungere tutti i simboli, non solo quelli usati, per la tabella dei simboli dinamica.

Questi non sono simboli di debug, sono simboli di linker dinamici. Quelle non vengono rimosse da strip poiché esso (nella maggior parte dei casi) interrompe l'eseguibile - vengono utilizzate dal linker di runtime per eseguire la fase di collegamento finale dell'eseguibile.

Esempio:

$ cat t.c 
void foo() {} 
int main() { foo(); return 0; } 

Compilare e collegare senza -rdynamic (e nessuna ottimizzazione, ovviamente)

$ gcc -O0 -o t t.c 
$ readelf -s t 

Symbol table '.dynsym' contains 3 entries: 
    Num: Value   Size Type Bind Vis  Ndx Name 
    0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 0000000000000000  0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
    2: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__ 

Symbol table '.symtab' contains 50 entries: 
    Num: Value   Size Type Bind Vis  Ndx Name 
    0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 0000000000400270  0 SECTION LOCAL DEFAULT 1 
.... 
    27: 0000000000000000  0 FILE LOCAL DEFAULT ABS t.c 
    28: 0000000000600e14  0 NOTYPE LOCAL DEFAULT 18 __init_array_end 
    29: 0000000000600e40  0 OBJECT LOCAL DEFAULT 21 _DYNAMIC 

Così l'eseguibile ha un .symtab con tutto. Ma notate che .dynsym non menziona lo foo - ha il minimo indispensabile lì dentro. Non sono sufficienti informazioni per il funzionamento di backtrace_symbols. Si basa sulle informazioni presenti in quella sezione per abbinare gli indirizzi di codice con i nomi di funzioni.

Ora compilare con -rdynamic:

$ gcc -O0 -o t t.c -rdynamic 
$ readelf -s t 

Symbol table '.dynsym' contains 17 entries: 
    Num: Value   Size Type Bind Vis  Ndx Name 
    0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 0000000000000000  0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
    2: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__ 
    3: 0000000000000000  0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 
    4: 0000000000601018  0 NOTYPE GLOBAL DEFAULT ABS _edata 
    5: 0000000000601008  0 NOTYPE GLOBAL DEFAULT 24 __data_start 
    6: 0000000000400734  6 FUNC GLOBAL DEFAULT 13 foo 
    7: 0000000000601028  0 NOTYPE GLOBAL DEFAULT ABS _end 
    8: 0000000000601008  0 NOTYPE WEAK DEFAULT 24 data_start 
    9: 0000000000400838  4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 
    10: 0000000000400750 136 FUNC GLOBAL DEFAULT 13 __libc_csu_init 
    11: 0000000000400650  0 FUNC GLOBAL DEFAULT 13 _start 
    12: 0000000000601018  0 NOTYPE GLOBAL DEFAULT ABS __bss_start 
    13: 000000000040073a 16 FUNC GLOBAL DEFAULT 13 main 
    14: 0000000000400618  0 FUNC GLOBAL DEFAULT 11 _init 
    15: 00000000004007e0  2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini 
    16: 0000000000400828  0 FUNC GLOBAL DEFAULT 14 _fini 

Symbol table '.symtab' contains 50 entries: 
    Num: Value   Size Type Bind Vis  Ndx Name 
    0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 0000000000400270  0 SECTION LOCAL DEFAULT 1 
.... 
    27: 0000000000000000  0 FILE LOCAL DEFAULT ABS t.c 
    28: 0000000000600e14  0 NOTYPE LOCAL DEFAULT 18 __init_array_end 
    29: 0000000000600e40  0 OBJECT LOCAL DEFAULT 21 _DYNAMIC 

Stessa cosa per i simboli in .symtab, ma ora foo ha un simbolo nella sezione simbolo dinamico (e un sacco di altri simboli appaiono anche ora). Questo rende il lavoro backtrace_symbols - ora ha abbastanza informazioni (nella maggior parte dei casi) per mappare gli indirizzi di codice con nomi di funzioni.

Striscia che:

$ strip --strip-all t 
$ readelf -s t 

Symbol table '.dynsym' contains 17 entries: 
    Num: Value   Size Type Bind Vis  Ndx Name 
    0: 0000000000000000  0 NOTYPE LOCAL DEFAULT UND 
    1: 0000000000000000  0 FUNC GLOBAL DEFAULT UND [email protected]_2.2.5 (2) 
    2: 0000000000000000  0 NOTYPE WEAK DEFAULT UND __gmon_start__ 
    3: 0000000000000000  0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses 
    4: 0000000000601018  0 NOTYPE GLOBAL DEFAULT ABS _edata 
    5: 0000000000601008  0 NOTYPE GLOBAL DEFAULT 24 __data_start 
    6: 0000000000400734  6 FUNC GLOBAL DEFAULT 13 foo 
    7: 0000000000601028  0 NOTYPE GLOBAL DEFAULT ABS _end 
    8: 0000000000601008  0 NOTYPE WEAK DEFAULT 24 data_start 
    9: 0000000000400838  4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used 
    10: 0000000000400750 136 FUNC GLOBAL DEFAULT 13 __libc_csu_init 
    11: 0000000000400650  0 FUNC GLOBAL DEFAULT 13 _start 
    12: 0000000000601018  0 NOTYPE GLOBAL DEFAULT ABS __bss_start 
    13: 000000000040073a 16 FUNC GLOBAL DEFAULT 13 main 
    14: 0000000000400618  0 FUNC GLOBAL DEFAULT 11 _init 
    15: 00000000004007e0  2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini 
    16: 0000000000400828  0 FUNC GLOBAL DEFAULT 14 _fini 
$ ./t 
$ 

No .symtab è andato, ma la tabella di simboli dinamica è ancora lì, e viene eseguito il Programma. Quindi funziona anche backtrace_symbols.

Striscia la tabella dinamica simbolo:

$ strip -R .dynsym t 
$ ./t 
./t: relocation error: ./t: symbol , version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference 

... e si ottiene un file eseguibile rotto.

Una lettura interessante per ciò che è utilizzato per .symtab e .dynsym è qui: Inside ELF Symbol Tables. Una delle cose da notare è che .symtab non è necessario in fase di esecuzione, quindi viene scartato dal caricatore. Quella sezione non rimane nella memoria del processo. .dynsym, d'altra parte, è necessario in fase di esecuzione, quindi viene mantenuto nell'immagine di processo. Quindi è disponibile per cose come backtrace_symbols per raccogliere informazioni sul processo corrente da dentro di sé.

Quindi in breve:

  • simboli dinamici non vengono rimosse da strip dal che renderebbe l'eseguibile non caricabile
  • backtrace_symbols esigenze simboli dinamici di capire quale codice frazione che funzionano
  • backtrace_symbols fa non utilizzare i simboli di debug

Quindi il comportamento che hai notato.


Per le vostre domande specifiche:

  1. gdb è un debugger. Usa le informazioni di debug nell'eseguibile e nelle librerie per visualizzare le informazioni rilevanti. È molto più più complesso di backtrace_symbols e ispeziona i file reali sul disco rigido in aggiunta al processo in tempo reale. backtrace_symbols no, è interamente in-process, quindi non può accedere a sezioni che non sono caricate nell'immagine eseguibile. Le sezioni di debug non vengono caricate nell'immagine runtime, quindi non possono essere utilizzate.
  2. .dynsym non è una sezione di debug. È una sezione utilizzata dal linker dinamico. .symbtab non è una sezione di debug, ma può essere utilizzata dal debugger che ha accesso ai file eseguibili (e alle librerie). -rdynamicnon genera sezioni di debug, solo quella tabella di simboli dinamici estesa. La crescita dell'eseguibile da -rdynamic dipende interamente dal numero di simboli nell'eseguibile (e dalle considerazioni di allineamento/riempimento). Dovrebbe essere considerevolmente inferiore a -g.
  3. Fatta eccezione per i file binari collegati in modo statico, gli eseguibili necessitano di dipendenze esterne risolte al momento del caricamento. Come il collegamento di printf e alcune procedure di avvio dell'applicazione dalla libreria C. Questi simboli esterni devono essere indicati da qualche parte nell'eseguibile: questo è ciò che viene utilizzato per .dynsym, ed è per questo che l'exe ha un .dynsym anche se non si specifica -rdynamic. Quando lo specifichi, il linker aggiunge altri simboli che non sono necessari perché il processo funzioni, ma può essere usato da cose come backtrace_symbols.
  4. backtrace_symbols non risolverà alcun nome di funzione se si collega staticamente. Anche se si specifica -rdynamic, la sezione .dynsym non verrà emessa nell'eseguibile. Nessuna tabella di simboli viene caricata nell'immagine eseguibile, quindi backtrace_symbols non può mappare gli indirizzi di codice ai simboli.
+0

grazie per la risposta .. ma io sono ancora piuttosto confuso .. Lasciami entrare le mie domande come una risposta separata in modo che possa essere formattata meglio. – Manohar

+0

Do ** not ** pubblica una risposta come followup, non è così che funziona questo sito. Modifica la tua domanda originale (o pubblica una nuova se la tua nuova domanda è sufficientemente diversa). – Mat

+0

@Santhosh: aggiunte alcune informazioni alla fine e ho cercato di chiarire il tutto. HTH. – Mat