3
votes

Où sont stockées les valeurs statiques dans l'assembly

Voici un simple code C

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 13
    .globl  _main                   ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    xorl    %eax, %eax
    movl    $0, -4(%rbp)
    movl    $30, -8(%rbp)
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _a                      ## @a
    .p2align    2
_a:
    .long   5                       ## 0x5



Compilé de manière fiable sans optimisation:

#include <stdio.h>

int a = 5;

static int b = 20;

int main(){

 int c = 30;

 return 0;
}

Ma question est de savoir où se trouve static int b = 20; dans l'assemblage ci-dessus? Je sais qu'ils sont censés être dans la section globale de la mémoire mais je ne peux pas les trouver dans la version compilée.


9 commentaires

Peut-être nulle part? Le compilateur l'a peut-être supprimé car vous ne l'utilisiez pas.


@immibis je pensais juste ça aussi. Même avec l'optimisation désactivée, je pensais que le compilateur ne ferait pas cela mais je ne le vois pas ainsi .... Joe les rend volatils et compare l'assemblage


je l'ai compilé sans optimisation activée


"Aucune optimisation activée" ne signifie pas "n'effectuer aucune transformation dans mon code". Il vous suffit de retourner b depuis main et vous le verrez apparaître.


@CarlNorumin fait ça, merci, je pensais qu'aucune optimisation ne signifie ne pas toucher le code et compiler tel quel


@Joe même à -O0 il y a des transformations faites comme divisions constantes par valeur de puissance de deux


Si le compilateur ne touchait pas au code, il produirait du code C au lieu de l'assembly et ce ne serait pas plus utile que cat . Quoi qu'il en soit, apparemment même -O0 fait quelques optimisations qui sont vraiment simples et évidentes pour le compilateur.


@Joe Quel serait le code d'assemblage qui correspond à la compilation tel que le nombre 20, non utilisé en aucune façon?


@DavidSchwartz: Il a _a: .long 5 pour la variable globale inutilisée. L'OP examine la sortie complète du langage d'assemblage du compilateur, pas seulement les mnémoniques du code machine + les opérandes. Les données doivent également être assemblées dans (généralement d'autres sections du) fichier de sortie, si elles sont présentes dans la source asm.


4 Réponses :



7
votes

Votre code n'utilise pas b , et il a une portée de fichier donc rien dans d'autres fichiers ne peut l'utiliser. GCC ne prend pas la peine d'en émettre une définition.

Pour répondre à la question du titre:
Une variable de variable statique / globale non const (c.-à-d. Classe de stockage statique) avec un initialiseur différent de zéro ira dans .section .data , par opposition à . bss (zero-init mutable), ou .rdata (Windows) / .rodata (Linux) pour les données non nulles en lecture seule.


gcc ne dispose pas d'un mode de lecture automatique entièrement translittéré en asm naïvement. Voir Désactiver toutes les options d'optimisation dans GCC - GCC doit toujours se transformer via ses représentations internes.

GCC fait toujours une passe qui laisse de côté les éléments inutilisés même à -O0 . Il pourrait y avoir un moyen de désactiver cela, contrairement à certaines des autres transformations que gcc fait même à -O0 .

gcc et clang -O0 compilent chaque instruction dans un bloc distinct d'ASM qui stocke / recharge tout ( pour un débogage cohérent ), mais dans ce bloc, gcc applique toujours ses transformations standard, comme (x + y) devenant y <0 pour les signés x et y avec gcc8 et plus récent , ou x / 10 en multiplication + décalage de la moitié haute. ( Pourquoi GCC utilise la multiplication par un nombre étrange lors de la mise en œuvre de la division entière? ).

Et le code à l'intérieur de if (false) est supprimé par gcc même à -O0 , donc vous ne pouvez pas sauter dessus dans GDB .

Certaines personnes se soucient des performances d'exécution des versions de débogage, en particulier les développeurs de logiciels en temps réel tels que des jeux ou des systèmes d'exploitation qui ne sont pas correctement testables s'ils s'exécutent trop lentement. (Interaction humaine dans les jeux, ou peut-être pilotes de périphérique dans les systèmes d'exploitation.)


Certains autres compilateurs sont plus braindead à -O0 , donc vous voyez souvent asm qui ressemble encore plus aux expressions sources. Je pense avoir vu MSVC sans optimisation émettre des instructions qui ont fait mov -immédiatement dans un registre, puis cmp reg, imm , c'est-à-dire faire une branche à l'exécution qui ne dépend que de immédiat, et aurait donc pu être calculé de manière triviale au moment de la compilation dans cette expression.

Et bien sûr, il existe des compilateurs vraiment non optimisateurs dont le but est simplement de translittérer avec des modèles fixes. Par exemple, le Tiny C Compiler , je pense, est à peu près en un seul passage, et émet asm (ou code machine) au fur et à mesure. Voir Le code généré par Tiny C Compiler émet un supplément (inutile ?) NOPs et JMPs montre à quel point c'est simpliste: il émet toujours un sub esp, imm32 dans les prologues de fonction, et ne revient que pour remplir l'immédiat à la fin de la fonction une fois qu'il sait combien de pile la fonction a besoin. Même si la réponse est zéro, il ne peut pas la supprimer et resserrer le code.


De toute façon, il est généralement plus intéressant de regarder un asm optimisé. Écrivez des fonctions qui prennent des arguments et renvoient une valeur, afin que vous puissiez voir la partie intéressante de l'asm sans beaucoup de bruit standard et de stockage / rechargement. Comment supprimer le "bruit" de la sortie de l'assembly GCC / clang? < / a>


1 commentaires

Techniquement, le / O0 de MSVC est le même que GCC et Clang. Il est conçu pour optimiser le code pour le débogage, afin que vous puissiez faire des choses comme définir des points d'arrêt sur des lignes individuelles de code C. Il n'élide pas les branches, mais il effectue un pliage arithmétique de base lors de la compilation. Voir, par exemple, ce code . Il sait que le conditionnel est toujours évalué à vrai, donc il ne fait pas réellement la comparaison des constantes, mais il émet du code qui fait un test-and-branch afin que vous puissiez définir un point d'arrêt sur le if . Et il n'élide jamais le code «mort».



-3
votes

Les variables statiques ne sont pas stockées dans la mémoire. Ils n'apparaîtront que lorsqu'ils sont utilisés Par exemple

int statique b = 20; c = c + b;

compilera

ajouter c, '20'


1 commentaires

Vous pensez à static const . Sans const , oui cette optimisation est possible si rien dans le fichier n'écrit la variable, mais ce n'est pas vrai dans le cas général, et gcc ne le fait pas sans l'optimisation activée: godbolt.org/z/LR5Uw7 . Notez le mov edx, DWORD PTR b [rip] . De plus, '20' est un littéral de caractères de 2 octets, avec les codes ASCII pour '2' et '0' . c'est-à-dire 0x3032 car x86 est petit-boutiste.



0
votes

Toute la question est un peu inexacte ... (en la relisant, vous êtes en fait très précis sur "dans l'assemblage ci-dessus" ... eh bien, alors la réponse est "nulle part" .. et le reste de ma réponse est pour la question qui n'a pas été postée, mais j'espère expliquer pourquoi "nulle part" n'est une réponse à votre question).

Vous avez une source C, puis vous affichez un assemblage en tant que sortie du compilateur (mais vous ne spécifiez pas le compilateur) et ensuite vous posez des questions sur l'assembly ...

Le C est en cours de définition sur une "machine abstraite C", pendant que vous regardez une implémentation x86-64 particulière d'une telle machine abstraite.

Bien que cette implémentation ait des règles où finissent généralement les variables statiques, cela dépend entièrement du compilateur - comment il veut les implémenter.

Dans l'assemblage pur (comme l'écrit à la main ou du point de vue du processeur), il n'y a pas de "valeur statique". Vous n'avez que des registres, de la mémoire et des périphériques.

Donc, dans Assembly (code machine), vous pouvez utiliser un certain registre ou une certaine partie de la mémoire comme variable statique. Celui qui convient le mieux à vos besoins (il n'y a pas de règle stricte qui vous obligerait à le faire d'une manière particulière, sauf que vous devez exprimer votre idée dans le code machine valide pour le processeur cible, mais cela signifie généralement qu'il y a des milliards de possibilités et même quand en vous contraignant uniquement à des "raisonnables", c'est encore plus vers des dizaines de voies possibles que sur une seule).

Vous pouvez (en x86-64) même créer un schéma peu compliqué comment garder la valeur comme code-state ("part of memory" est alors la mémoire occupée par le code machine), c'est à dire qu'elle ne serait pas directement écrite en mémoire en tant que valeur, mais le code suivrait certains chemins de code (parmi plusieurs possibles) pour obtenir un résultat final correct, c'est-à-dire encoder la valeur dans le code lui-même. Il existe par exemple un moyen complet de Turing de compiler la source C en code machine x86-64 en utilisant uniquement l'instruction mov , qui n'utilise peut-être pas la mémoire pour les variables statiques (pas sûr, si elle ajoute .data ou évitez-le en le compilant également dans le code mov , mais de par sa pure existence, il devrait être assez évident comment le .data peut être théoriquement évité ).

Vous vous demandez donc comment un compilateur C particulier avec des options de compilation particulières implémente des valeurs statiques (et qui peut avoir des variantes en fonction de la source et des options utilisées) ...

... ou si vous vous demandez vraiment "où sont les valeurs statiques stockées dans l'assemblage", alors la réponse est "où vous le souhaitez, tant que votre code machine est valide et correct" , comme l'ensemble du concept de «valeur statique» est d'un niveau plus élevé que celui auquel le processeur fonctionne, c'est donc comme l'interprétation de l'objectif du code machine particulier «c'est la valeur statique», mais il n'y a pas d'instructions / de support spécifiques dans le processeur pour gérer cela.


0 commentaires