2008-09-29 5 views
101

Ich versuche, etwas Montage zu verstehen.`testl` eax gegen eax?

Die Montage wie folgt, ich bin an der testl Linie:

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

Ich versuche, diesen Punkt von testl zwischen %eax und %eax zu verstehen? Ich denke, die Besonderheiten dessen, was dieser Code ist nicht wichtig, ich versuche nur, den Test mit sich selbst zu verstehen - wäre der Wert nicht immer wahr?

Antwort

73

Es wird geprüft, ob eax 0 oder höher oder niedriger ist. In diesem Fall wird der Sprung ausgeführt, wenn eax 0 ist.

+0

Ich machte eine Bearbeitung, um diese populäre Antwort in eine bessere kanonische Antwort auf "was ist das TEST-Ding alles über, und wie ist es anders als CMP", was implizit ist. Siehe meine eigene Antwort weiter unten für Kommentare zur semantischen Bedeutung von JE und JZ. Bitte überprüfe meine Bearbeitung, da sie ziemlich wichtig ist und immer noch deine Antwort ist. –

+0

@PeterCordes Ich schätze die Absicht, aber ich werde Ihre Bearbeitung rückgängig machen. 1. Ihre "Stimme" ist sehr verschieden von meiner, und im Moment liest es sich viel mehr wie Ihre Antwort als meine. 2. Problematischer ist die fette Behauptung, dass die Flags zwischen "test" und "cmp" genau gleich herauskommen. Ja, ich verstehe, dass Sie aufgrund Ihrer Kommentare zu Cody davon überzeugt sind. Es ist jedoch etwas anderes, es in meinen Posten zu legen. es ist keine Behauptung, ich bin bereit zu stehen, einfach weil ich nicht weiß, ob es in allen Fällen identisch ist. –

+0

@PeterCordes Ich verstehe den Wunsch, eine "kanonische" Antwort zu haben, aber ich denke, Ihre Antwort muss auf eigene Kosten schwimmen oder sinken, anstatt auf eine akzeptierte Antwort zu paddeln. Ich musste das auch für einige meiner Antworten auf sehr populäre Fragen tun, wie zum Beispiel die [floating-point primer one] (http://stackoverflow.com/a/27030789/13), die zum Zeitpunkt der Veröffentlichung meiner Antwort hatte bereits andere Antworten mit 500 Upvotes. –

82

Die Bedeutung von test ist, AND die Argumente zusammen, und überprüfen Sie das Ergebnis für Null. Also testet dieser Code, ob EAX Null ist oder nicht. je springt bei Null.

BTW, dies erzeugt einen kleineren Befehl als cmp eax, 0, was der Grund dafür ist, dass Compiler dies normalerweise tun.

3

Wenn EAX Null ist es, den bedingten Sprung durchführen wird, sonst wird es die Ausführung bei 319e9

31

Der Testbefehl weiterhin tut eine logische UND-Verknüpfung zwischen den Operanden aber schreibt nicht das Ergebnis zurück in ein Register. Nur die Flags werden aktualisiert.

In Ihrem Beispiel wird der Test eax, eax das Null-Flag setzen, wenn eax Null ist, das Vorzeichen-Flag, wenn das höchste Bit gesetzt ist und einige andere Flags.

Der Sprung bei gleichem (je) Befehl springt, wenn das Null-Flag gesetzt ist.

Sie können den Code zu einem besser lesbaren Code wie folgt übersetzen:

cmp eax, 0 
je somewhere 

, dass die gleiche Funktionalität erfordert aber einige Bytes mehr Code-Raum. Aus diesem Grund hat der Compiler einen Test anstelle eines Vergleichs ausgegeben.

+3

Eigentlich könnte cmp dort nicht funktionieren. Das heißt, es funktioniert für den spezifizierten Fall, aber cmp beeinflusst Flags anders als Test, weil es ein internes Sub anstelle von und ist. Etwas, das man im Kopf behalten sollte. –

+4

für einen Test gegen Null ist es vollkommen gültig. –

+3

Aber Sie wissen nicht, was sonst die Flaggen später sieht. Die Auswirkungen auf Flaggen sind sehr unterschiedlich, so dass dies ein Problem sein kann und sehr häufig ist. –

5

Dieser Codeschnipsel stammt aus einer Subroutine, der ein Zeiger auf etwas gegeben wurde, wahrscheinlich eine Struktur oder ein Objekt. Die zweite Zeile dereferenziert den Zeiger, indem er einen Wert von diesem Ding abruft - möglicherweise selbst ein Zeiger oder vielleicht nur ein int, der als zweites Glied gespeichert wird (Offset +4). Die dritte und vierte Zeile testen diesen Wert für Null (NULL, wenn es ein Zeiger ist) und überspringt die folgenden wenigen Operationen (nicht gezeigt), wenn es Null ist.

Der Test für Null wird manchmal als Vergleich zu einem unmittelbaren Literalwert von Null codiert, aber der Compiler (oder Mensch?), Der dies geschrieben hat, hätte gedacht, dass ein Testl schneller laufen würde - unter Berücksichtigung aller modernen CPU-Sachen wie Pipelining und Registerumbenennung. Es ist aus der gleichen Trickkiste, die die Idee hat, ein Register mit XOR EAX, EAX (das ich auf jemandes Nummernschild in Colorado sah!) Statt der offensichtlichen, aber vielleicht langsameren MOV EAX, # 0 (ich benutze eine ältere Notation) zu löschen).

In Asm, wie Perl, TMTOWTDI.

15

test ist wie and, außer dass es nur FLAGS schreibt, wobei beide Eingänge unverändert bleiben. Mit zwei verschiedenen Eingängen ist es nützlich zu testen, ob einige Bits alle Null sind oder ob mindestens eine gesetzt ist. (z.B.test al, 3 setzt ZF, wenn EAX ein Vielfaches von 4 ist (und somit beide seiner niedrigen 2 Bits auf Null gesetzt sind).


test eax,eax setzt alle Flags genau die gleiche Art und Weise, dass cmp eax, 0 würde:

  • CF und geräumte (AND/Test nicht immer, dass, und Null Subtraktion erzeugt nie einen Übertrag)
  • ZF, SF und PF nach dem Wert in EAX. (a = a&a = a-0)

(Mit Ausnahme der veralteten AF (Auxiliary-Carry-Flag verwendet, um ASCII/BCD Anweisungen). TEST leaves it undefined, aber CMP sets it "according to the result". Null, da Subtrahieren eines Übertrag von der vierten bis fünften Bit nicht produzieren kann, CMP sollte immer AF löschen).


TEST kleiner (keine unmittelbare) und manchmal schneller (kann Makro-Sicherung in einen Vergleichs- und Zweig UOP auf mehreren CPUs in mehr Fällen als CMP). That makes test the preferred idiom for testing a register for zero or not. Der einzige allgemeine Grund für die Verwendung von CMP mit einer unmittelbaren 0 ist, wenn Sie mit einem Speicheroperanden vergleichen möchten (z. B. cmpb $0, (%esi), um nach einem abschließenden Nullbyte am Ende einer C-style-Zeichenfolge mit impliziter Länge zu suchen). .


AVX512F fügt kortestw k1, k2 und AVX512DQ/BW (Skylake aber nicht KNL) in ktestb/w/d/q k1, k2, die auf AVX512 Maskenregister (k0..k7) arbeiten, aber immer noch gesetzt regelmäßige FLAGS wie test tut, die gleiche Weise, Ganzzahl OR oder AND Anweisungen tun.

kortestw k1,k1 ist der idiomatische Weg/verzweigen cmovcc/setcc basierend auf einem Ergebnis AVX512 vergleichen, ersetzt SSE/AVX2 (v)pmovmskb/ps/pd + test oder cmp.


Verwendung von jz vs. je kann verwirrend sein.

jz and je are literally the same instruction, d.h. derselbe Opcode im Maschinencode. Sie tun das gleiche, aber haben unterschiedliche semantische Bedeutung für Menschen. Disassemblierer (und typischerweise asm, die von Compilern ausgegeben werden) verwenden immer nur einen, so dass die semantische Unterscheidung verloren geht.

cmp und sub setzen Sie ZF, wenn ihre beiden Eingänge gleich sind (d. H. Das Subtraktionsergebnis ist 0). je (Sprung wenn gleich) ist das semantisch relevante Synonym.

test %eax,%eax/and %eax,%eax setzt erneut ZF, wenn das Ergebnis Null ist, aber es gibt keinen "Gleichheitstest". ZF nach Test sagt Ihnen nicht, ob die beiden Operanden gleich waren. Also jz (Sprung wenn Null) ist das semantisch relevante Synonym.

+0

Ich würde in Betracht ziehen, die grundlegenden Informationen über 'test' bitweises' und 'Operation hinzufügen, kann nicht offensichtlich für Menschen nur Lernen Montage sein (und faul/nicht bewusst alle 60 Sekunden die Anweisung Reference Guide zu überprüfen;) :)). – Ped7g

+1

@ Ped7g: Fair genug, ich denke, es tut nicht weh, alles in diese Antwort zu setzen, anstatt diesen Teil zu den anderen Antworten zu verlassen. AVX512 'kortest *' und 'ktest *' hinzugefügt, während ich dabei war. –

0

In einigen Programmen können sie verwendet werden, um nach einem Pufferüberlauf zu suchen. Ganz oben im zugewiesenen Bereich wird eine 0 platziert.Nach dem Eingeben von Daten in den Stapel wird nach der 0 am Anfang des zugewiesenen Speicherplatzes gesucht, um sicherzustellen, dass der zugewiesene Speicherplatz nicht übergelaufen ist.

Es wurde in der stack0 Ausübung von Exploits-Übungen verwendet zu überprüfen, ob es überschwemmt wurde und wenn es gab nicht und es gab es eine Null ist, wäre es „Versuchen Sie es erneut“ angezeigt

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

Ich sehe nicht, was dieser spezielle Fall der Überprüfung eines Registers für Nicht-Null zu diesem Q & A hinzufügt. Vor allem, wenn 'cmp DWORD PTR [esp + 0x5c], 0' /' jz 0x8048427 'effizienter gewesen wäre als eine separate MOV-Last und dann TEST. Dies ist kaum ein üblicher Anwendungsfall zum Überprüfen auf eine Null. –

-2

wir konnten sehen, die jg, jle wenn testl %edx,%edx. jle .L3 wir einfach jle finden konnten ist Klage (SF^OF)|ZF, wenn% edx Null ist, ZF = 1, aber wenn% edx nicht Null und -1 ist, nach dem testl, die OF = 0, und der SF = 1, also das Flag = wahr, das implementieren Sprung .sorry, mein Englisch ist schlecht

Verwandte Themen