Je voudrais une explication des valeurs utilisées avec les directives .cfi_def_cfa_offset dans Assembly générées par GCC. Je sais vaguement que les directives .cfi sont impliquées dans les cadres d’appel et le déroulement de la pile, mais je voudrais une explication plus détaillée de la raison pour laquelle, par exemple, les valeurs 16 et 8 sont utilisées dans l’Assembly fourni par GCC lors de la compilation du programme C suivant sur ma machine Ubuntu 64 bits.
Le programme C:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("%d", 0);
return 0;
}
J'ai appelé GCC sur le fichier source test.c comme suit: gcc -S -O3 test.c
. Je sais que -O3 permet une optimisation non standard, mais je voulais limiter la taille de l’Assembly généré par souci de brièveté.
L'assemblée générée:
.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
Pourquoi les valeurs 16 et 8 sont-elles utilisées pour les directives .cfi_def_cfa_offset dans l'assembly généré? Aussi, pourquoi le nombre 22 utilisé pour les étiquettes de début et de fin de fonction locale?
Comme le spécifie DWARF dit dans la section 6.4:
[...] La trame d'appel est identifié par une adresse sur la pile. Nous appelons cette adresse le cadre canonique Adresse ou CFA. En règle générale, le CFA est défini comme étant la valeur du pointeur de pile au niveau du appelez le site dans la trame précédente (qui peut être différent de sa valeur lors de la saisie dans la trame courante ).
main()
est appelé ailleurs (dans le code de support d'exécution libc
C), et, au moment de l'exécution de l'instruction call
, %rsp
pointe vers le haut de la pile (l'adresse la plus basse - la pile croît vers le bas), quelle que soit cela peut être (exactement ce que c'est n'a pas d'importance ici):
: : ^
| whatever | <--- %rsp | increasing addresses
+----------------+ |
La valeur de %rsp
à ce stade est la "valeur du pointeur de pile sur le site d’appel", c’est-à-dire le CFA tel que défini par la spéc.
Lorsque l'instruction call
est exécutée, une adresse de retour de 64 bits (8 octets) est insérée dans la pile:
: :
| whatever | <--- CFA
+----------------+
| return address | <--- %rsp == CFA - 8
+----------------+
Nous exécutons maintenant le code à la variable main
, qui exécute subq $8, %rsp
pour réserver 8 octets de pile supplémentaires:
: :
| whatever | <--- CFA
+----------------+
| return address |
+----------------+
| reserved space | <--- %rsp == CFA - 16
+----------------+
Le pointeur de changement de pile est déclaré dans les informations de débogage à l'aide de la directive .cfi_def_cfa_offset
et vous pouvez voir que le CFA est maintenant à un décalage de 16 octets par rapport au pointeur de pile actuel.
À la fin de la fonction, l'instruction addq $8, %rsp
modifie à nouveau le pointeur de pile. Une autre directive .cfi_def_cfa_offset
est donc insérée pour indiquer que le CFA est maintenant à un décalage de seulement 8 octets par rapport au pointeur de pile.
(Le nombre "22" dans les étiquettes est simplement une valeur arbitraire. Le compilateur générera des noms d'étiquette uniques en fonction de certains détails d'implémentation, tels que la numérotation interne des blocs de base.)
Je voudrais une explication des valeurs utilisées avec les directives
.cfi_def_cfa_offset
dans Assembly générées par GCC.
Matthew a fourni une bonne explication. Voici la définition de Section 7.10 Directives CFI dans le manuel GAS:
.cfi_def_cfa_offset
modifie une règle de calcul de CFA. Le registre reste le même, mais l'offset est nouveau. Notez que c'est le décalage absolu qui sera ajouté à un registre défini pour calculer l'adresse CFA.
Et .cfi_adjust_cfa_offset
:
Identique à
.cfi_def_cfa_offset
mais offset est une valeur relative ajoutée/soustraite de l'offset précédent.