2011-09-23 19 views
39

Vorrei una spiegazione per i valori utilizzati con le direttive .cfi_def_cfa_offset nell'assembly generato da GCC. So vagamente che le direttive .cfi sono coinvolte nei call frames e nello stack unwinding, ma vorrei una spiegazione più dettagliata del perché, ad esempio, i valori 16 e 8 sono utilizzati nell'assembly fornito da GCC nella compilazione del seguente programma C sulla mia macchina Ubuntu a 64 bit.GAS: spiegazione di .cfi_def_cfa_offset

Il programma C:

#include <stdio.h> 

int main(int argc, char** argv) 
{ 
     printf("%d", 0); 
     return 0; 
} 

ho invocato GCC sul file test.c fonte come segue: gcc -S -O3 test.c. So che -O3 consente l'ottimizzazione non standard, ma volevo limitare la dimensione dell'assemblaggio generato per brevità.

L'assembly generato:

 .file "test.c" 
     .section  .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
     .string "%d" 
     .text 
     .p2align 4,,15 
.globl main 
     .type main, @function 
main: 
.LFB22: 
     .cfi_startproc 
     subq $8, %rsp 
     .cfi_def_cfa_offset 16 
     xorl %edx, %edx 
     movl $.LC0, %esi 
     movl $1, %edi 
     xorl %eax, %eax 
     call __printf_chk 
     xorl %eax, %eax 
     addq $8, %rsp 
     .cfi_def_cfa_offset 8 
     ret 
      .cfi_endproc 
.LFE22: 
     .size main, .-main 
     .ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2" 
     .section  .note.GNU-stack,"",@progbits 

Perché sono i valori di 16 e 8 utilizzati per le direttive .cfi_def_cfa_offset in assembly generato? Inoltre, perché il numero 22 utilizzato per la funzione locale inizia e le etichette di fine funzione?

risposta

61

Come DWARF spec dice nella sezione 6.4:

[...] Il telaio chiamata è identificato da un indirizzo in pila. Ci riferiamo a questo indirizzo come l'Indirizzo Canonial Frame o CFA. In genere, il CFA è definito come il valore del puntatore dello stack nel sito di chiamata nel frame precedente (che potrebbe essere diverso dal suo valore in ingresso nel frame corrente ).

main() è chiamato da qualche altra parte (nel codice supporto runtime libc C), e, al momento viene eseguita l'istruzione call, %rsp punterà alla cima della pila (che è l'indirizzo più basso - il pila cresce verso il basso), qualunque essa sia (esattamente quello che è non importa qui):

:    :       ^
| whatever | <--- %rsp     | increasing addresses 
+----------------+        | 

il valore della %rsp a questo punto è il "valore del puntatore stack al sito chiamata", vale a dire il CFA come definito dalla specifica.

Come viene eseguita l'istruzione call, spingerà un (8 byte) indirizzo di ritorno a 64 bit nello stack:

:    : 
| whatever | <--- CFA 
+----------------+ 
| return address | <--- %rsp == CFA - 8 
+----------------+ 

Ora ci sono in esecuzione il codice a main, che esegue subq $8, %rsp riservare un'altra 8 byte di stack per sé:

:    : 
| whatever | <--- CFA 
+----------------+ 
| return address | 
+----------------+ 
| reserved space | <--- %rsp == CFA - 16 
+----------------+ 

il cambio di stack pointer è dichiarato nelle informazioni di debug usando la direttiva .cfi_def_cfa_offset, e si può vedere che il CFA è ora ad un n offset di 16 byte dal puntatore dello stack corrente.

Al termine della funzione, l'istruzione addq $8, %rsp cambia di nuovo il puntatore dello stack, quindi viene aggiunta un'altra direttiva .cfi_def_cfa_offset per indicare che il CFA ha ora un offset di soli 8 byte dal puntatore dello stack.

(Il numero "22" nelle etichette è solo un valore arbitrario.Il compilatore genererà nomi delle etichette uniche basate su qualche dettaglio attuazione, come la sua numerazione interna dei blocchi di base.)

+3

Davvero un'ottima spiegazione. Comunemente le etichette sono numerate sequenzialmente (riguardo allo scope delle funzioni), qui potremmo vedere solo quelle perché l'ottimizzatore ha rimosso le altre etichette. – JohnTortugo

+0

Grazie mille per una spiegazione molto chiara! – namanhams

+2

Per comprendere le direttive .cfi_ *, dovresti anche controllare https://sourceware.org/binutils/docs/as/CFI-directives.html. È magro, ma è ufficiale. –

1

desidero una spiegazione per i valori utilizzati con le .cfi_def_cfa_offset direttive assembly generati da GCC.

Matthew ha fornito una buona spiegazione. Ecco la definizione dal Section 7.10 CFI Directives nel manuale GAS:

.cfi_def_cfa_offset modifica di una regola per calcolare CFA. Il registro rimane lo stesso, ma l'offset è nuovo. Si noti che è l'offset assoluto che verrà aggiunto a un registro definito per calcolare l'indirizzo CFA.

E .cfi_adjust_cfa_offset:

Uguale .cfi_def_cfa_offset ma offset è un valore relativo che viene aggiunto/sottratto dal precedente offset.