web-dev-qa-db-fra.com

GAS: explication de .cfi_def_cfa_offset

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?

43
void-pointer

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.)

70
Matthew Slattery

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. 

1
jww