2011-09-23 23 views
39

Me gustaría obtener una explicación de los valores utilizados con las directivas .cfi_def_cfa_offset en el ensamblado generado por GCC. Sé vagamente que las directivas .cfi están involucradas en los marcos de llamada y el desenrollado de la pila, pero me gustaría una explicación más detallada de por qué, por ejemplo, los valores 16 y 8 se usan en el ensamblado generado por GCC al compilar el siguiente programa C. en mi máquina Ubuntu de 64 bits.GAS: Explicación de .cfi_def_cfa_offset

El programa C:

#include <stdio.h> 

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

que invoca GCC en el archivo test.c fuente de la siguiente manera: gcc -S -O3 test.c. Sé que -O3 permite la optimización no estándar, pero quería limitar el tamaño del ensamblaje generado en aras de la brevedad.

El conjunto generado:

 .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 

¿Por qué son los valores 16 y 8 utilizados para las directivas .cfi_def_cfa_offset en el conjunto generado? Además, ¿por qué el número 22 utilizado para la función local comienza y las etiquetas de función final?

Respuesta

61

A medida que el DWARF spec dice en la sección 6.4:

[...] El marco de llamada es identificado por una dirección en la pila. Nos referimos a esta dirección como la dirección del Marco Canónico o CFA. Normalmente, el CFA se define como el valor del puntero de pila en el sitio de llamada en el marco anterior (que puede ser diferente de su valor en la entrada al marco actual ).

main() se llama desde otro lugar (en el código de soporte de ejecución libc C), y, a la vez que se ejecuta la instrucción call, %rsp apuntará a la parte superior de la pila (que es la dirección más baja - la pila crece hacia abajo), sea lo que sea (exactamente lo que es no importa aquí):

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

el valor de %rsp en este momento es el "valor del puntero de pila en el lugar llamado", es decir, el CFA según lo definido por la especificación.

A medida que se ejecuta la instrucción call, empujará un 64 bits (8 bytes) remite a la pila:

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

Ahora que se están ejecutando el código en main, que ejecuta subq $8, %rsp a reservar otra 8 bytes de pila por sí mismo:

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

el cambio de puntero de pila se declara en la información de depuración usando la directiva .cfi_def_cfa_offset, y se puede ver que el CFA se encuentra ahora en una n desplazamiento de 16 bytes desde el puntero de pila actual.

Al final de la función, la instrucción addq $8, %rsp cambia el puntero de la pila nuevamente, por lo que se inserta otra directiva .cfi_def_cfa_offset para indicar que el CFA está ahora a un desplazamiento de solo 8 bytes del puntero de la pila.

(El número "22" en las etiquetas es solo un valor arbitrario.El compilador generará nombres de etiqueta únicos basados ​​en algún detalle de implementación, tales como su numeración interna de los bloques básicos.)

+3

De hecho, una muy buena explicación. Comúnmente, las etiquetas se numeran de forma secuencial (en relación con el alcance de la función); en este caso, solo podemos verlas porque el optimizador eliminó las otras etiquetas. – JohnTortugo

+0

¡Muchas gracias por una explicación muy clara! – namanhams

+2

Para comprender las directivas .cfi_ *, también debe consultar https://sourceware.org/binutils/docs/as/CFI-directives.html. Es delgado, pero es oficial. –

1

me gustaría una explicación de los valores utilizados con los .cfi_def_cfa_offset directivas en conjunto generados por GCC.

Matthew proporcionó una buena explicación. Esta es la definición de Section 7.10 CFI Directives en el manual GAS:

.cfi_def_cfa_offset modifica una regla para el cálculo de CFA. El registro sigue siendo el mismo, pero el desplazamiento es nuevo. Tenga en cuenta que es el desplazamiento absoluto que se agregará a un registro definido para calcular la dirección CFA.

Y .cfi_adjust_cfa_offset:

Igual que .cfi_def_cfa_offset pero desplazamiento es un valor relativo que se agrega/sustraído de la anterior offset.