Perché GDB ha bisogno dell'eseguibile e del core dump?


11

Sto eseguendo il debug utilizzando core dump e noto che gdb ha bisogno che tu fornisca l'eseguibile e il core dump. Perchè è questo? Se il dump principale contiene tutta la memoria utilizzata dal processo, l'eseguibile non è contenuto nel dump principale? Forse non c'è garanzia che l'intero exe sia caricato in memoria (i singoli eseguibili non sono di solito così grandi) o forse il dump del core non contiene tutta la memoria rilevante dopo tutto? È per i simboli (forse non sono caricati normalmente nella memoria)?


1
L'eseguibile contiene le informazioni sul simbolo, come indicato nella documentazione di gdb ...
Thomas Dickey,

1
Sorprendentemente, nessuna risposta (tranne quella che ho appena aggiunto) menziona il formato
DWARF

Risposte:


15

Il core dump è solo il dump dell'impronta di memoria dei tuoi programmi, se sai dove si trovava tutto, puoi semplicemente usarlo.

Si utilizza l'eseguibile perché spiega dove (in termini di indirizzi logici) le cose si trovano in memoria, cioè il file principale.

Se si utilizza un comando objdump, verranno scaricati i metadati sull'oggetto eseguibile che si sta esaminando. Utilizzando un oggetto eseguibile chiamato a.out come esempio.

objdump -h a.outscarica solo le informazioni dell'intestazione, vedrai le sezioni chiamate ad es. .data o .bss o .text (ce ne sono molti altri). Questi informano il caricatore del kernel dove nell'oggetto possono essere trovate varie sezioni e dove nello spazio degli indirizzi di processo la sezione dovrebbe essere caricata, e per alcune sezioni (es. .Data .text) cosa dovrebbe essere caricato. (la sezione .bss non contiene alcun dato nel file ma fa riferimento alla quantità di memoria da riservare nel processo per i dati non inizializzati, è piena di zeri).

Il layout del file oggetto eseguibile è conforme a un ELF standard.

objdump -x a.out - scarica tutto

Se l'oggetto eseguibile contiene ancora le sue tabelle dei simboli (non è stato rimosso - man stripe hai usato -gper generare la generazione di debug gcc assumendo la compilazione della fonte ac), allora puoi esaminare il contenuto principale con i nomi dei simboli, ad esempio se avevi una variabile / buffer chiamato inputLine nel codice sorgente, è possibile utilizzare quel nome gdbper visualizzarne il contenuto. vale a dire gdbconoscere l'offset dall'inizio del programma inizializzato il segmento di dati in cui inizia inputLine e la lunghezza di quella variabile.

Ulteriori letture articolo 1 , articolo 2 e per la specifica grintosa Executable and Linking Format (ELF) .


Aggiornamento dopo il commento @mirabilos di seguito.

Ma se si utilizza la tabella dei simboli come in

$ gdb --batch -s a.out -c core -q -ex "x buf1"

produce

 0x601060 <buf1>:    0x72617453

e quindi non usando la tabella dei simboli e esaminando l'indirizzo direttamente in,

$ gdb --batch -c core -q -ex "x 0x601060"

produce

0x601060:   0x72617453

Ho esaminato la memoria direttamente senza usare la tabella dei simboli nel 2 ° comando.


Vedo anche che la risposta di @ user580082 aggiunge ulteriore spiegazione e voterà.


6
Mai sentito parlare di "sezione stack di base". .bss è (storicamente) "blocco avviato dal simbolo" e praticamente "dati unitizzati", mentre .data è "dati inizializzati" e il testo (non .code) viene utilizzato per memorizzare il codice macchina. Non esiste una sezione di stack in un file binario, poiché gli stack vengono creati in fase di esecuzione.
jlliagre,

"Se sai dove si trovava tutto, allora potresti semplicemente usarlo" non è vero neanche perché non tutto ciò che è nel programma è necessariamente incluso nell'impronta.
mirabilos,

1
@jlliagre hai ragione, ho erroneamente chiamato .text .code (perché stavo pensando a una spiegazione mentre componevo la risposta) - aggiornato. Ho erroneamente pensato a bss in modo errato per nome e ho aggiornato la mia risposta, ma ho evitato * Blocco avviato da Symbol poiché non penso che si aggiunga realmente all'equazione e ho spiegato che è utilizzato come dati non inizializzati, che era il nostro comprensione comune. Grazie. Ho apprezzato molto il tuo commento per correggere questa pubblicazione.
X Tian,

4

Il file principale è un'istantanea dell'immagine dello stack, dei mapping di memoria e dei registri al momento della conclusione del processo. Il cui contenuto può essere manipolato come indicato nella pagina man di base . Per impostazione predefinita, i mapping privati, i mapping condivisi e le informazioni dell'intestazione ELF vengono scaricati nel file core.

Venendo alla tua domanda , la ragione per cui gdb richiede eseguibile è perché non simula l'esecuzione, leggendo e interpretando le istruzioni binarie come fa valgrind invece diventa il genitore del processo in modo da controllare il comportamento del processo durante l'esecuzione tempo. Utilizza il file core per determinare i mapping di memoria e lo stato del processo del processore durante l'arresto anomalo.

In Linux i processi parent possono ottenere ulteriori informazioni sui propri figli, in particolare la possibilità di individuarli che consentono al debugger di accedere alle informazioni di basso livello del processo come leggere / scrivere la sua memoria, i registri, modificare i mapping dei segnali, interromperne l'esecuzione, ecc.

Capirai il requisito dell'eseguibile nonostante di avere più file core una volta letto come funziona qualsiasi debugger.


1

(oltre ad altre buone risposte)

Sui moderni sistemi Linux (e molti sistemi simili a Unix), le informazioni di debug (inclusi metadati sui tipi di simboli, posizione del codice sorgente, tipo di variabili, ecc. Ecc.) Sono in formato DWARF e si trovano all'interno dell'eseguibile ELF ( o librerie condivise ELF) quando viene compilato con alcune -gopzioni. Consiglio di compilare programmi con cui eseguire il debug -g3 -O0e magari -fno-inlinese si utilizza un GCC recente ; tuttavia, con GCC puoi persino compilare sia le informazioni di ottimizzazione che quelle di debug, ad esempio con -O2 -g1, sebbene le informazioni di debug in quel caso possano essere un po '"sfocate" (questo potrebbe aiutare leggermente a catturare alcuni Heisenbugs cattivi ).

È abbastanza ragionevole evitare di inserire tali informazioni nei file core , perché potresti avere molti file core diversi (immagina un software ampiamente usato con molti utenti che fanno segnalazioni di bug, la maggior parte con un coredump) per lo stesso eseguibile. Anche i file core (5) vengono scaricati dal kernel, il che non dovrebbe interessare dell'esistenza di sezioni DWARF negli eseguibili elf (5) (poiché queste sezioni non sono mappate nello spazio di indirizzi virtuale del processo di errore che ha scaricato core su alcuni segnali ( 7) ). Esiste persino la possibilità di inserire le informazioni di debug in file separati (al di fuori dell'eseguibile).

A proposito, GDB può essere dolorosamente utilizzato per il debug di core dump per eseguibili senza alcuna informazione di debug. Ma in pratica esegui il debug a livello di codice macchina (non a livello simbolico fornito dai linguaggi di programmazione e dai loro compilatori).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.