assembly callstack gas dwarf

assembly - GAS: Explicación de.cfi_def_cfa_offset



callstack dwarf (2)

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.

El programa C:

#include <stdio.h> int main(int argc, char** argv) { printf("%d", 0); return 0; }

Invoqué a GCC en el archivo fuente test.c 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 ensamblaje 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é se utilizan los valores 16 y 8 para las directivas .cfi_def_cfa_offset en el ensamblaje generado? Además, ¿por qué el número 22 utilizado para la función local comienza y las etiquetas de función final?


Me gustaría obtener una explicación de los valores utilizados con las directivas .cfi_def_cfa_offset en el ensamblado generado por GCC.

Matthew brindó una buena explicación. Aquí está la definición de la Sección 7.10 Directivas CFI en el manual de GAS:

.cfi_def_cfa_offset modifica una regla para calcular 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 el desplazamiento es un valor relativo que se suma / resta del desplazamiento anterior.


Como dice la especificación DWARF en la sección 6.4:

[...] El marco de llamada se identifica por una dirección en la pila. Nos referimos a esta dirección como Canonical Frame Address o CFA. Típicamente, el CFA se define como el valor del puntero de la pila en el sitio de la llamada en el cuadro anterior (que puede ser diferente de su valor en la entrada al cuadro actual).

main() se llama desde otro lugar (en el código de compatibilidad de tiempo de ejecución de libc C) y, en el momento en que se ejecuta la instrucción de 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 punto es el "valor del puntero de la pila en el sitio de la llamada", es decir, el CFA tal como lo define la especificación.

A medida que se ejecuta la instrucción de call , empujará una dirección de retorno de 64 bits (8 bytes) a la pila:

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

Ahora estamos ejecutando el código en main , que ejecuta subq $8, %rsp para reservar otros 8 bytes de stack 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 utilizando la directiva .cfi_def_cfa_offset , y puede ver que el CFA ahora tiene un desplazamiento de 16 bytes desde el puntero de pila actual.

Al final de la función, la addq $8, %rsp cambia el puntero de la pila de nuevo, por .cfi_def_cfa_offset 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 algunos detalles de implementación, como su numeración interna de bloques básicos).