`testl` eax contra a eax?

Eu estou tentando entender alguma assembly.

A assembly da seguinte forma, estou interessado na linha testl :

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

Eu estou tentando entender esse ponto de testl entre %eax e %eax ? Eu acho que as especificidades do que este código não é importante, eu estou apenas tentando entender o teste consigo mesmo – o valor não seria sempre verdadeiro?

Ele testa se eax é 0, ou acima, ou abaixo. Neste caso, o salto é tomado se o eax for 0.

O significado do test é para AND os argumentos juntos e verifique o resultado para zero. Então este código testa se o EAX é zero ou não. je vai pular se zero.

BTW, isso gera uma instrução menor que o cmp eax, 0 que é a razão pela qual os compiladores geralmente farão isso dessa maneira.

A instrução de teste faz uma operação AND lógica entre os operandos, mas não grava o resultado de volta em um registrador. Apenas os sinalizadores são atualizados.

Em seu exemplo, o teste eax, eax definirá o sinalizador zero se eax for zero, o sinalizador se o bit mais alto for definido e alguns outros sinalizadores também.

A instrução Jump if Equal (je) salta se o sinalizador zero estiver definido.

Você pode traduzir o código para um código mais legível como este:

 cmp eax, 0 je somewhere 

Isso tem a mesma funcionalidade, mas requer alguns bytes a mais de espaço de código. Essa é a razão pela qual o compilador emitiu um teste em vez de uma comparação.

test é como and , exceto que ele apenas grava FLAGS, deixando ambas as inputs não modificadas. Com duas inputs diferentes , é útil para testar se alguns bits são todos zero, ou se pelo menos um está definido. (Por exemplo, test al, 3 conjuntos ZF se EAX é um múltiplo de 4 (e, portanto, tem ambos os seus 2 bits baixos zerados).


test eax,eax define todas as flags exatamente da mesma maneira que cmp eax, 0 :

  • CF e OF limpas (AND / TEST sempre faz isso; e subtraindo zero nunca produz um carry)
  • ZF, SF e PF de acordo com o valor em EAX. ( a = a&a = a-0 )

(Exceto pelo AF obsoleto (sinalizador auxiliar-carry, usado por instruções ASCII / BCD). TEST deixa indefinido , mas o CMP o define “de acordo com o resultado” .Uma vez que subtrair zero não pode produzir um carry do 4º ao 5º. bit, o CMP deve sempre limpar AF).


O TEST é menor (não imediato) e às vezes mais rápido (pode macro-fusível em um uop de comparação e ramificação em mais CPUs em mais casos que o CMP). Isso faz do test o idioma preferido para testar um registro para zero ou não .

O único motivo comum para usar o CMP com um 0 imediato é quando você deseja comparar com um operando de memory (por exemplo, cmpb $0, (%esi) para verificar um byte zero de terminação no final de um estilo C de comprimento implícito corda).


O AVX512F adiciona o kortestw k1, k2 e AVX512DQ / BW (Skylake mas não KNL) e adiciona ktestb/w/d/q k1, k2 , que operam em registros de máscara AVX512 (k0..k7) mas ainda definem FLAGS regulares como o test , da mesma maneira que instruções integer OR ou AND .

kortestw k1,k1 é a maneira idiomática de ramificar / cmovcc / setcc com base em um resultado de comparação AVX512, substituindo SSE / AVX2 (v)pmovmskb/ps/pd + test ou cmp .


O uso de jz vs. je pode ser confuso.

jz e je são literalmente a mesma instrução , isto é, o mesmo opcode no código da máquina. Eles fazem a mesma coisa, mas têm significado semântico diferente para os humanos . Desassembladores (e tipicamente saídas as dos compiladores) só usarão um, então a distinção semântica é perdida.

cmp e sub ajustam ZF quando suas duas inputs são iguais (isto é, o resultado da subtração é 0). je (jump if equal) é o sinônimo semanticamente relevante.

test %eax,%eax / and %eax,%eax define ZF novamente quando o resultado é zero, mas não há teste de “igualdade”. ZF após o teste não informa se os dois operandos são iguais. Então jz (pula se zero) é o sinônimo semanticamente relevante.

Este trecho de código é de uma sub-rotina que recebeu um ponteiro para algo, provavelmente alguma estrutura ou object. A segunda linha desreferencia esse ponteiro, buscando um valor dessa coisa – possivelmente um ponteiro ou talvez apenas um int, armazenado como seu segundo membro (offset +4). A terceira e a quarta linhas testam esse valor para zero (NULL se for um ponteiro) e pulam as poucas operações a seguir (não mostradas) se forem zero.

O teste para zero às vezes é codificado como uma comparação com um valor zero literal imediato, mas o compilador (ou humano?) Que escreveu isso pode ter pensado que um teste seria executado mais rápido – levando em consideração todas as coisas modernas da CPU como pipelining e register renomeando. É do mesmo saco de truques que mantém a idéia de limpar um registro com XOR EAX, EAX (que eu vi na matrícula de alguém no Colorado!) Ao invés do óbvio, mas talvez mais lento MOV EAX, # 0 (eu uso uma notação mais antiga ).

Em conjunto, como perl, TMTOWTDI.

Se eax for zero, ele executará o salto condicional, caso contrário, ele continuará a execução em 319e9

Em alguns programas, eles podem ser usados ​​para verificar um estouro de buffer. No topo do espaço alocado, um 0 é colocado. Depois de inserir dados na pilha, ele procura o 0 no início do espaço alocado para garantir que o espaço alocado não seja excedido.

Foi usado no exercício stack0 dos exploits-exercises para verificar se estava transbordando e se não existisse e houvesse um zero lá, ele mostraria “Try again”

 0x080483f4 
: push ebp 0x080483f5
: mov ebp,esp 0x080483f7
: and esp,0xfffffff0 0x080483fa
: sub esp,0x60 0x080483fd
: mov DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack 0x08048405
: lea eax,[esp+0x1c] 0x08048409
: mov DWORD PTR [esp],eax 0x0804840c
: call 0x804830c 0x08048411
: mov eax,DWORD PTR [esp+0x5c] 0x08048415
: test eax,eax ; checks if its zero 0x08048417
: je 0x8048427
0x08048419
: mov DWORD PTR [esp],0x8048500 0x08048420
: call 0x804832c 0x08048425
: jmp 0x8048433
0x08048427
: mov DWORD PTR [esp],0x8048529 0x0804842e
: call 0x804832c 0x08048433
: leave 0x08048434
: ret

nós poderíamos ver o jg , testl %edx,%edx. jle .L3 Se testl %edx,%edx. jle .L3 testl %edx,%edx. jle .L3 poderíamos facilmente achar jle é terno (SF^OF)|ZF , se% edx for zero, ZF = 1, mas se% edx não for zero e for -1, após o teste, o OF = 0, e o SF = 1, então o flag = true, que implementa o salto .Desculpe, meu inglês é ruim