2008-09-29 18 views
101

J'essaie de comprendre un peu d'assemblage.`testl` eax contre eax?

L'assemblée comme suit, je suis intéressé par la ligne testl:

000319df 8b4508  movl 0x08(%ebp), %eax 
000319e2 8b4004  movl 0x04(%eax), %eax 
000319e5 85c0   testl %eax, %eax 
000319e7 7407   je  0x000319f0 

J'essaie de comprendre ce point de testl entre %eax et %eax? Je pense que les spécificités de ce que ce code n'est pas important, j'essaie juste de comprendre le test avec lui-même - la valeur ne serait-elle pas toujours vraie?

Répondre

73

Il vérifie si eax est égal ou supérieur à 0 ou inférieur. Dans ce cas, le saut est pris si eax est 0.

+0

J'ai fait un montage pour transformer cette réponse populaire en une meilleure réponse canonique à "quoi est-ce que tout cela TEST, et en quoi est-il différent de CMP", qui est en quelque sorte implicite. Voir ma propre réponse plus bas pour des commentaires sur la signification sémantique du JE et JZ synonyme. S'il vous plaît consulter mon édition, car il est assez important, et c'est toujours votre réponse. –

+0

@PeterCordes J'apprécie l'intention, mais je vais revenir à votre édition. 1. Votre «voix» est très différente de la mienne, et en ce moment, elle ressemble beaucoup plus à votre réponse qu'à la mienne. 2. Plus problématique est l'affirmation audacieuse que les drapeaux sortent exactement de la même manière entre 'test' et' cmp'. Oui, je comprends que c'est votre croyance basée sur vos commentaires à Cody. Cependant, le mettre dans mon poste est une question différente; ce n'est pas une affirmation que je suis prêt à accepter, simplement parce que je ne sais pas si c'est identique dans tous les cas. –

+0

@PeterCordes Je comprends le souhait d'avoir une réponse "canonique", mais je pense que votre réponse doit nager ou couler sur ses propres mérites, plutôt que de se greffer sur une réponse acceptée. J'ai dû faire cela pour certaines de mes réponses à des questions très populaires aussi, comme [l'amorce à virgule flottante] (http://stackoverflow.com/a/27030789/13), qui au moment de poster mon réponse déjà eu d'autres réponses avec 500+ upvotes. –

82

La signification de test est de ET les arguments ensemble, et vérifier le résultat pour zéro. Donc, ce code teste si EAX est nul ou non. je va sauter si zéro. BTW, cela génère une instruction plus petite que cmp eax, 0 qui est la raison pour laquelle les compilateurs vont généralement le faire de cette façon.

3

Si EAX est égal à zéro, il effectuera le saut conditionnel, sinon il continuera l'exécution à 319e9

31

L'instruction de test fait un ET logique coopération entre les opérandes mais ne pas écrire le résultat dans un registre. Seuls les drapeaux sont mis à jour.

Dans votre exemple, le test eax, eax définira le drapeau zéro si eax est zéro, le drapeau de signe si le bit le plus élevé est défini et d'autres indicateurs également.

L'instruction Jump si Equal (je) saute si le drapeau zéro est activé.

Vous pouvez traduire le code à un code plus lisible comme ceci:

cmp eax, 0 
je somewhere 

qui a la même fonctionnalité, mais nécessite quelques octets de plus d'espace de code. C'est la raison pour laquelle le compilateur a émis un test au lieu d'un compare.

+3

En fait, cmp pourrait ne pas fonctionner là. C'est-à-dire, cela fonctionne pour le cas spécifique présenté, mais cmp affecte les drapeaux différemment que le fait le test, car il s'agit d'un sous-système interne au lieu de et. Quelque chose à garder à l'esprit. –

+4

pour un test contre zéro c'est parfaitement valide. –

+3

Mais vous ne savez pas quoi d'autre regarde les drapeaux plus tard. Les effets sur les drapeaux sont très différents, donc cela peut être un problème et très fréquemment. –

5

Cet extrait de code provient d'un sous-programme qui a reçu un pointeur sur quelque chose, probablement une structure ou un objet. La deuxième ligne déréférence ce pointeur, récupérant une valeur de cette chose - peut-être elle-même un pointeur ou peut-être juste un int, stocké comme son 2ème membre (offset +4). Les 3ème et 4ème lignes testent cette valeur pour zéro (NULL si c'est un pointeur) et sautent les quelques opérations suivantes (non montrées) si elle est nulle. Le test de zéro est parfois codé comme une comparaison à une valeur zéro littérale immédiate, mais le compilateur (ou humain?) Qui a écrit cela pourrait avoir pensé qu'un testl fonctionnerait plus vite - en tenant compte de toutes les choses de l'UC moderne comme pipelining et registre renommer. C'est à partir du même sac que l'idée d'effacer un registre avec XOR EAX, EAX (que j'ai vu sur la plaque d'immatriculation de quelqu'un dans le Colorado!) Plutôt que MOV EAX, # 0 (j'utilise une notation plus ancienne).

Dans asm, comme perl, TMTOWTDI.

15

test est comme and, sauf qu'il n'écrit que FLAGS, laissant ses deux entrées non modifiées. Avec deux différentes entrées, il est utile de tester si certains bits sont tous zéro, ou si au moins un est défini. (par exemple.test al, 3 définit ZF si EAX est un multiple de 4 (et donc a ses deux bits 2 bas mis à zéro).


test eax,eax ensembles tous les drapeaux de la même manière que cmp eax, 0 serait:

  • CF et DE classer (ET/TEST fait toujours que, et en soustrayant jamais nul produit un report)
  • ZF, SF et PF en fonction de la valeur dans EAX. (a = a&a = a-0)

(A l'exception du obsolète AF (drapeau auxiliaire carry, utilisé par des instructions ASCII/BCD). TEST leaves it undefined, mais CMP sets it "according to the result". Comme soustraction zéro ne peut pas produire un report du 4 au 5 bit, CMP devrait toujours effacer AF).


TEST est plus petit (pas immédiat) et parfois plus rapide (peut macro-fusible dans une comparaison et branche UOP sur plusieurs unités centrales dans plus de cas que CMP). That makes test the preferred idiom for testing a register for zero or not. La seule raison courante pour utiliser CMP avec un 0 immédiat est lorsque vous voulez comparer avec un opérande de mémoire (par exemple, cmpb $0, (%esi) pour vérifier un octet de fin à la fin d'une chaîne de style C de longueur implicite) .


AVX512F ajoute kortestw k1, k2 et AVX512DQ/BW (Skylake mais pas KNL) ajouter ktestb/w/d/q k1, k2, qui fonctionnent sur les registres de masque (AVX512 de k0..k7), mais encore FLAGS réguliers comme définis test le fait, de la même façon que nombre entier OR ou AND instructions faire.

kortestw k1,k1 est la manière idiomatique à la branche/cmovcc/SetCC basé sur un AVX512 comparer les résultats, en remplaçant SSE/AVX2 (v)pmovmskb/ps/pd + test ou cmp.


Utilisation de jz contre je peut être source de confusion.

jz and je are literally the same instruction, c'est-à-dire le même opcode dans le code machine. Ils font la même chose, mais ont une signification sémantique différente pour les humains. Les désassembleurs (et généralement la sortie asm des compilateurs) n'en utiliseront qu'un seul, donc la distinction sémantique est perdue.

cmp et sub définissez ZF lorsque leurs deux entrées sont égales (c'est-à-dire que le résultat de la soustraction est 0). je (saut si égal) est le synonyme sémantiquement pertinent.

test %eax,%eax/and %eax,%eax définit à nouveau ZF lorsque le résultat est zéro, mais il n'y a pas de test "d'égalité". ZF après le test ne vous dit pas si les deux opérandes étaient égaux. Donc jz (sauter si zéro) est le synonyme sémantiquement pertinent.

+0

Je considérerais ajouter les informations de base sur le 'test' étant bitwise' et' opération, peut ne pas être évident pour les personnes qui apprennent simplement l'assemblage (et être paresseux/inconscient pour vérifier le guide de référence d'instruction toutes les 60 secondes;) :)). – Ped7g

+1

@ Ped7g: assez juste, je suppose que ça ne fait pas de mal de tout mettre dans cette réponse, au lieu de laisser cette partie aux autres réponses. Ajout de AVX512 'kortest *' et 'ktest *' pendant que j'y étais. –

0

Dans certains programmes, ils peuvent être utilisés pour vérifier un dépassement de tampon. Tout en haut de l'espace alloué, un 0 est placé.Après avoir entré les données dans la pile, il recherche le 0 au tout début de l'espace alloué pour s'assurer que l'espace alloué n'est pas débordé.

Il a été utilisé dans l'exercice de stack0 des exploits des exercices pour vérifier si elle a été débordait et s'il n'y avait pas et il y avait un zéro là, il afficherait « Réessayez »

0x080483f4 <main+0>: push ebp 
0x080483f5 <main+1>: mov ebp,esp 
0x080483f7 <main+3>: and esp,0xfffffff0 
0x080483fa <main+6>: sub esp,0x60      
0x080483fd <main+9>: mov DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack 
0x08048405 <main+17>: lea eax,[esp+0x1c] 
0x08048409 <main+21>: mov DWORD PTR [esp],eax 
0x0804840c <main+24>: call 0x804830c <[email protected]> 
0x08048411 <main+29>: mov eax,DWORD PTR [esp+0x5c] 
0x08048415 <main+33>: test eax,eax     ; checks if its zero 
0x08048417 <main+35>: je  0x8048427 <main+51> 
0x08048419 <main+37>: mov DWORD PTR [esp],0x8048500 
0x08048420 <main+44>: call 0x804832c <[email protected]> 
0x08048425 <main+49>: jmp 0x8048433 <main+63> 
0x08048427 <main+51>: mov DWORD PTR [esp],0x8048529 
0x0804842e <main+58>: call 0x804832c <[email protected]> 
0x08048433 <main+63>: leave 
0x08048434 <main+64>: ret 
+0

Je ne vois pas ce que ce cas particulier de vérification d'un registre pour non-zéro ajoute à ce Q & A. Surtout quand 'cmp DWORD PTR [esp + 0x5c], 0' /' jz 0x8048427 'aurait été plus efficace qu'une charge MOV séparée, puis TEST. Ce n'est guère un cas d'utilisation courant pour vérifier un zéro. –

-2

nous avons pu voir la jg, jle Si testl %edx,%edx. jle .L3 on pouvait trouver facilement jle est costume (SF^OF)|ZF, si% EDX est égal à zéro, ZF = 1, mais si% EDX est non nul et -1, après la testl, le DE = 0, et le SF = 1, donc le drapeau = vrai, que l'outil de saut . Désolé, mon anglais est pauvre