8
votes

Examen du code généré par le compilateur Visual Studio C ++, partie 1

Dupliqué possible: strong>
Pourquoi ce code complexe est-il émis pour diviser un entier signé par une puissance de deux? p>

fond h2>

Je vais simplement apprendre x86 ASM en examinant le code binaire généré par le compilateur. p>

code compilé à l'aide du compilateur C ++ dans Visual Studio 2010 Beta 2 . P>

0040100A  |. 8D9B 00000000  LEA EBX,DWORD PTR DS:[EBX]
00401010  |> 99             /CDQ
00401011  |. 2BC2           |SUB EAX,EDX


0 commentaires

4 Réponses :


5
votes

Le LEA EBX, [EBX] est juste une opération NOP. Son but est d'aligner le début de la boucle en mémoire, ce qui le rendra plus rapide. Comme vous pouvez le constater ici, le début de la boucle commence à l'adresse 0x00401010, qui est divisible par 16, grâce à cette instruction.

Le CDQ et Sub EAX, EDX Opérations Assurez-vous que la Division arrondira un nombre négatif sur zéro - sinon, SAR SAR serait arrondir, donnant des résultats incorrects pour négatif chiffres.


5 commentaires

Mauvais: EBX est utilisé par __tmaincrtstartu pour vérifier s'il s'agit d'une application gérée, ce n'est donc pas un NOP et n'a rien d'emballage avec l'alignement


Vous vous trompe vous-même. Lea EBX, [EBX] est équivalent à MOV EBX, EBX - Il ne fait rien. La valeur du registre EBX n'est pas vérifiée pour quoi que ce soit.


Il ne semble rien faire car Ebx est initié avec zéro (XOR EBX, EBX) dans __tmaincrTstartup avant que le principal est appelé. Démontez simplement le code dans __tmaincrtstartup et vous verrez. Dès que les retours principaux, EBX est vérifié. Si vous par exemple. Attribuez explicitement à EBX avant de revenir en main, le compilateur conservera la valeur de EBX (zéro pour l'application native) et la restaurera lorsque le rendement principal.


Jn: Le fait que EBX détient une valeur est hors de propos, car cette instruction ne le comparait pas avec quoi que ce soit ou modifier. Cela aurait pu accéder à tout autre registre. Si vous regardez le code créé par Visual Studio, vous verrez ce type de code avant la plupart des boucles intérieures - et le début de la boucle sera aligné sur 16 (ou peut-être 8) octets.


Interjay: Vous avez raison lorsque vous dites que cette opération est utilisée pour aligner la boucle. L'insertion des NOPS au début rend VS Insérer lea ECX, [ECX + 0]. D'autre part, EBX est en cause utilisé par le temps d'exécution pour vérifier s'il s'agit d'une application gérée. L'arrêt du temps d'exécution est traité différemment s'il est



12
votes

Le tout

y = (y + (y < 0)) >> 1;


1 commentaires

Pas -2/2 + 1 , mais plutôt (- 2 + 1) >> 1 . Ceci évalue à -1 .



1
votes

Cela ne répond pas vraiment à la question, mais est un indice utile. Au lieu de vous brouiller avec la chose ollydbg.exe, vous pouvez créer un studio visuel générer le fichier ASM pour vous, qui a le bonus ajouté qu'il peut placer dans le code source d'origine comme des commentaires. Ce n'est pas une grosse affaire pour votre petit projet actuel, mais comme votre projet se développe, vous risquez de passer à une juste durée de déterminer quel code d'assemblage correspond à quel code source.

de la ligne de commande, vous voulez le Options FAS et / FA ( MSDN ) . P>

Voici une partie de la sortie pour votre code d'exemple (j'ai compilé le code de débogage, de sorte que l'.asm est plus long, mais vous pouvez faire la même chose pour votre code optimisé): P>

_wmain  PROC                        ; COMDAT

; 8    : {

    push    ebp
    mov ebp, esp
    sub esp, 216                ; 000000d8H
    push    ebx
    push    esi
    push    edi
    lea edi, DWORD PTR [ebp-216]
    mov ecx, 54                 ; 00000036H
    mov eax, -858993460             ; ccccccccH
    rep stosd

; 9    :     int x=5; int y=1024;

    mov DWORD PTR _x$[ebp], 5
    mov DWORD PTR _y$[ebp], 1024        ; 00000400H
$LN2@wmain:

; 10   :     while(x) { x--; y/=2; }

    cmp DWORD PTR _x$[ebp], 0
    je  SHORT $LN1@wmain
    mov eax, DWORD PTR _x$[ebp]
    sub eax, 1
    mov DWORD PTR _x$[ebp], eax
    mov eax, DWORD PTR _y$[ebp]
    cdq
    sub eax, edx
    sar eax, 1
    mov DWORD PTR _y$[ebp], eax
    jmp SHORT $LN2@wmain
$LN1@wmain:

; 11   :     return x+y;

    mov eax, DWORD PTR _x$[ebp]
    add eax, DWORD PTR _y$[ebp]

; 12   : }

    pop edi
    pop esi
    pop ebx
    mov esp, ebp
    pop ebp
    ret 0
_wmain  ENDP


2 commentaires

Merci, c'est très utile. J'utilise OllyDBG car c'est un outil aussi soigné, il met en évidence les valeurs de registre modifiées par la dernière instruction, attire les instructions JMP ira, pour moi, il est beaucoup plus pratique de suivre les instructions de Olly que dans toute autre chose.


Oh oui, pas de question Un débogueur complet de montage comme OLLYDBG est un excellent outil. Je suggérais simplement un autre outil pour votre boîte à outils.



2
votes

La raison pour laquelle le compilateur émet ceci: xxx

au lieu de l'équivalent sémantique: xxx

.. est-ce que c'est plus rapide pour le processeur pour exécuter une instruction de 6 octets à six instructions de 1 octet. C'est tout.


0 commentaires