`testl` eax contro eax?


118

Sto cercando di capire un po 'di assemblaggio.

L'assemblaggio come segue, mi interessa la testllinea:

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

Sto cercando di capire quel punto testltra %eaxe %eax? Penso che le specifiche di ciò che questo codice non sia importante, sto solo cercando di capire il test con se stesso - il valore non sarebbe sempre vero?

Risposte:


91

Verifica se eaxè 0, superiore o inferiore. In questo caso, il salto viene eseguito se eaxè 0.


2
Ho apportato una modifica per trasformare questa risposta popolare in una migliore risposta canonica a "cos'è questa cosa del TEST e in che modo è diversa da CMP", il che è in qualche modo implicito. Vedere la mia risposta più in basso per commenti sul significato semantico dei sinonimi JE e JZ. Si prega di rivedere la mia modifica poiché è piuttosto importante ed è ancora la tua risposta.
Peter Cordes

@PeterCordes Apprezzo l'intenzione, ma ho intenzione di annullare la tua modifica. 1. La tua "voce" è molto diversa dalla mia, e in questo momento sembra molto più simile alla tua risposta che alla mia. 2. Più problematica è l'affermazione audace che le bandiere escono esattamente allo stesso modo tra teste cmp. Sì, capisco che questa sia la tua convinzione sulla base dei tuoi commenti a Cody. Tuttavia, metterlo nel mio post è una questione diversa; non è un'affermazione che sono disposto a sostenere, semplicemente perché non so se è identica in tutti i casi.
Chris Jester-Young,

1
@PeterCordes Se trovo un po 'di tempo libero, voglio che questa risposta sia più canonica. Lo scriverei come lo scrivo io, però, e sono piuttosto esigente su come scrivo le cose. :-) Per esempio, mi piacerebbe scrivere je, jz, cmp, e test, e non JE, JZ, CMP, o TEST. Sono schizzinoso così.
Chris Jester-Young,

1
Non stavo cercando di rafforzare la mia risposta. In realtà ho dimenticato di aver risposto io stesso a questa domanda quando ho fatto quella modifica, e l'ho notato solo in seguito. Ho appena guardato questo dopo che qualcuno lo ha urtato, e quella che era iniziata come una piccola modifica si è trasformata in troppo. Nessuna offesa per il fatto che volevi tornare indietro; era solo un suggerimento e sicuramente si legge come il mio lavoro, non il tuo. Prenderò parte di ciò che ho scritto e lo inserirò nella mia risposta.
Peter Cordes

2
Wow, dopo aver modificato la mia risposta a questa domanda per includere ciò che ho aggiunto alla tua, mi sono reso conto di aver duplicato quasi esattamente la maggior parte di ciò che ho scritto a giugno. Oops! L'ho aggiornato con più ragionamenti per eseguire il backup della mia affermazione test a,ae cmp $0,aimpostare i flag in modo identico; grazie per aver sottolineato che è un'affermazione non banale. re: TEST vs test.: recentemente ho iniziato a usare tutte le maiuscole come i manuali di Intel. Ma quando parlo di mnemonici AT&T e mnemonici Intel, uso lo testbstile per AT&T. IDK se questo aiuta la leggibilità.
Peter Cordes

90

Il significato di testè quello di AND gli argomenti insieme e controllare il risultato per zero. Quindi questo codice verifica se EAX è zero o meno. jesalterà se zero.

A proposito, questo genera un'istruzione più piccola di quella cmp eax, 0che è la ragione per cui i compilatori generalmente lo fanno in questo modo.


34

L'istruzione di test esegue un'operazione logica AND tra gli operandi ma non riscrive il risultato in un registro. Vengono aggiornati solo i flag.

Nel tuo esempio il test eax, eax imposterà il flag zero se eax è zero, il flag del segno se è impostato il bit più alto e anche altri flag.

L'istruzione Jump if Equal (je) salta se è impostato il flag zero.

Puoi tradurre il codice in un codice più leggibile come questo:

cmp eax, 0
je  somewhere

Ha la stessa funzionalità ma richiede alcuni byte in più di spazio codice. Questo è il motivo per cui il compilatore ha emesso un test invece di un confronto.


3
In realtà, cmp potrebbe non funzionare lì. Cioè, funziona per il caso specifico presentato, ma cmp influisce sui flag in modo diverso rispetto a test, poiché è un sub interno invece di e. Qualcosa da tenere a mente.
Cody Brocious,

4
per un test contro zero è perfettamente valido.
Nils Pipenbrinck,

3
Ma non sai cos'altro guarda le bandiere in seguito. Gli effetti sulle bandiere sono molto diversi, quindi questo può essere un problema e molto spesso lo è.
Cody Brocious,

2
No, gli unici flag impostati da un diverso / method / sono carry e overflow, entrambi impostati a 0. I / values ​​/ degli altri flag saranno diversi perché cmp usa sub e test usa e.
Cody Brocious,

2
@CodyBrocious: test eax, eaxed cmp eax, 0entrambi impostano tutti i flag e li impostano su valori identici. Entrambe le istruzioni impostano tutti i flag "in base al risultato". La sottrazione 0non può mai produrre carry o overflow. Il tuo argomento è corretto per qualsiasi altro immediato diverso da 0, ma non per 0.
Peter Cordes

22

testè come and, tranne che scrive solo FLAGS, lasciando entrambi i suoi input non modificati. Con due diversi ingressi, è utile per verificare se alcuni bit sono tutti nulli o se almeno uno è impostato. (es. test al, 3imposta ZF se EAX è un multiplo di 4 (e quindi ha entrambi i suoi 2 bit bassi azzerati).


test eax,eaximposta tutte le bandiere esattamente nello stesso modo in cui cmp eax, 0avrebbe :

  • CF e OF cancellati (AND / TEST lo fa sempre; la sottrazione di zero non produce mai un riporto)
  • ZF, SF e PF in base al valore in EAX. ( a = a&a = a-0).
    (PF come al solito è impostato solo in base agli 8 bit bassi )

Fatta eccezione per l'obsoleto AF (flag di trasporto ausiliario, utilizzato dalle istruzioni ASCII / BCD). TEST lo lascia indefinito , ma CMP lo imposta "in base al risultato" . Poiché la sottrazione di zero non può produrre un riporto dal 4 ° al 5 ° bit, CMP dovrebbe sempre cancellare AF.


TEST è più piccolo (non immediato) e talvolta più veloce (può fondersi in macro in un uop di confronto e diramazione su più CPU in più casi rispetto a CMP). Questo rende testl'idioma preferito per confrontare un registro con lo zero . È un'ottimizzazione dello spioncino cmp reg,0che puoi utilizzare indipendentemente dal significato semantico.

L'unico motivo comune per utilizzare CMP con uno 0 immediato è quando si desidera confrontare con un operando di memoria. Ad esempio, cmpb $0, (%esi)per verificare la presenza di uno zero byte di terminazione alla fine di una stringa in stile C di lunghezza implicita.


AVX512F aggiungekortestw k1, k2 e AVX512DQ / BW (Skylake-X ma non KNL) ktestb/w/d/q k1, k2, che opera sui registri maschera AVX512 (k0..k7) ma imposta comunque FLAG regolari come testfanno, allo stesso modo degli interi ORo delle ANDistruzioni. (Un po 'come SSE4 ptesto SSE ucomiss: input nel dominio SIMD e risultato in FLAG interi.)

kortestw k1,k1è il modo idiomatico di branch / cmovcc / setcc basato su un risultato di confronto AVX512, sostituendo SSE / AVX2 (v)pmovmskb/ps/pd+ testo cmp.


L'uso di jzvs. jepuò creare confusione.

jze jesono letteralmente la stessa istruzione , cioè lo stesso codice operativo nel codice macchina. Fanno la stessa cosa, ma hanno un significato semantico diverso per gli esseri umani . I disassemblatori (e in genere l'output asm dai compilatori) ne useranno solo uno, quindi la distinzione semantica è persa.

cmpe subimpostare ZF quando i loro due input sono uguali (cioè il risultato della sottrazione è 0). je(salta se uguale) è il sinonimo semanticamente rilevante.

test %eax,%eax/ and %eax,%eaximposta nuovamente ZF quando il risultato è zero, ma non esiste un test di "uguaglianza". ZF dopo il test non ti dice se i due operandi erano uguali. Quindi jz(salta se zero) è il sinonimo semanticamente rilevante.


Considererei l'aggiunta delle informazioni di base sull'operazione testbit per bit and, potrebbe non essere ovvio per le persone che stanno solo imparando l'assemblaggio (ed essendo pigri / inconsapevoli di controllare la guida di riferimento delle istruzioni ogni 60 secondi;) :)).
Ped7g

1
@ Ped7g: abbastanza giusto, immagino che non faccia male mettere tutto in questa risposta, invece di lasciare quella parte alle altre risposte. Aggiunto AVX512 kortest*e già che ktest*ci ero.
Peter Cordes

A proposito, questo è fondamentalmente lo stesso della mia risposta a un'altra versione della stessa domanda , ma ho detto più cose sulle prestazioni lì, ad esempio possibilmente evitando gli stalli di lettura del registro sulle vecchie CPU della famiglia P6 come Nehalem riscrivendo il registro con lo stesso valore.
Peter Cordes

@PeterCordes Questa dovrebbe essere la risposta accettata: esaustiva e tecnica. A differenza del post accettato, questo placa la curiosità e la sete di conoscenza. Continui così signore.
programmatori

Va notato che PF è impostato alla parità degli 8 bit bassi, che in questo caso è AL.
ecm

5

Questo frammento di codice proviene da una subroutine a cui è stato dato un puntatore a qualcosa, probabilmente qualche struttura o oggetto. La seconda riga dereferenzia quel puntatore, recuperando un valore da quella cosa - forse essa stessa un puntatore o forse solo un int, memorizzato come suo secondo membro (offset +4). La terza e la quarta riga verificano questo valore per zero (NULL se è un puntatore) e saltano le seguenti poche operazioni (non mostrate) se è zero.

Il test per zero a volte è codificato come confronto con un valore letterale zero immediato, ma il compilatore (o umano?) Che ha scritto questo potrebbe aver pensato che un'operazione di test sarebbe stata eseguita più velocemente, prendendo in considerazione tutte le cose moderne della CPU come pipelining e register ridenominazione. Viene dalla stessa borsa di trucchi che contiene l'idea di cancellare un registro con XOR EAX, EAX (che ho visto sulla targa di qualcuno in Colorado!) Piuttosto che l'ovvio ma forse più lento MOV EAX, # 0 (uso una notazione precedente ).

In asm, come perl, TMTOWTDI.


3

Se eax è zero, eseguirà il salto condizionale, altrimenti continuerà l'esecuzione a 319e9


0

In alcuni programmi possono essere utilizzati per verificare un overflow del buffer. In cima allo spazio allocato viene posizionato uno 0. Dopo aver inserito i dati nello stack, cerca lo 0 all'inizio dello spazio allocato per assicurarsi che lo spazio allocato non sia in overflow.

È stato utilizzato nell'esercizio stack0 di exploit-esercizi per verificare se era in overflow e se non c'era e c'era uno zero, mostrava "Riprova"

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 <gets@plt>
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 <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret

Non vedo cosa questo caso specifico di controllo di un registro per un valore diverso da zero aggiunge a questa domanda e risposta. Soprattutto quando cmp DWORD PTR [esp+0x5c], 0/ jz 0x8048427 <main+51>sarebbe stato più efficiente di un carico MOV separato e quindi TEST. Questo non è certo un caso d'uso comune per il controllo dello zero.
Peter Cordes

-4

potremmo vedere jgjle Se testl %edx,%edx. jle .L3potessimo facilmente trovare jle è seme (SF^OF)|ZF, se% edx è zero, ZF = 1, ma se% edx non è zero ed è -1, dopo il testl, OF = 0 e SF = 1, quindi il flag = true, che implementa il salto. Scusa, il mio inglese è scarso

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.