`testl` eax contra eax?
-
02-07-2019 - |
Pregunta
Estoy tratando de entender un poco de ensamblaje.
El ensamblaje de la siguiente manera, estoy interesado en la línea testl
:
000319df 8b4508 movl 0x08(%ebp), %eax
000319e2 8b4004 movl 0x04(%eax), %eax
000319e5 85c0 testl %eax, %eax
000319e7 7407 je 0x000319f0
Estoy tratando de entender ese punto de testl
entre % eax
y % eax
? Creo que los detalles de lo que este código no es importante, solo trato de entender la prueba por sí mismo, ¿no sería el valor siempre verdadero?
Solución
Comprueba si eax
es 0, o superior, o inferior. En este caso, el salto se realiza si eax
es 0.
Otros consejos
El significado de test
es para AND los argumentos juntos, y verifica el resultado para cero. Entonces este código prueba si EAX es cero o no. je
saltará si es cero.
Por cierto, esto genera una instrucción más pequeña que cmp eax, 0
, que es la razón por la que los compiladores generalmente lo hacen de esta manera.
La instrucción de prueba realiza una operación AND lógica entre los operandos, pero no vuelve a escribir el resultado en un registro. Solo se actualizan las banderas.
En su ejemplo, la prueba eax, eax establecerá la bandera de cero si eax es cero, la bandera de signo si se establece el bit más alto y algunas otras banderas también.
La instrucción Saltar si igual (je) salta si se establece el indicador cero.
Puedes traducir el código a un código más legible como este:
cmp eax, 0
je somewhere
Eso tiene la misma funcionalidad pero requiere algunos bytes más de espacio de código. Esa es la razón por la que el compilador emitió una prueba en lugar de una comparación.
test
es como y
, excepto que solo escribe FLAGS, dejando ambas entradas sin modificar. Con dos entradas diferentes , es útil para probar si algunos bits son todos cero, o si al menos uno está configurado. (por ejemplo, test al, 3
establece ZF si EAX es un múltiplo de 4 (y por lo tanto tiene sus 2 bits bajos puestos a cero).
prueba eax, eax
establece todos señala exactamente de la misma forma que cmp eax, 0
sería :
- CF y OF borrados (AND / TEST siempre hace eso; y restar cero nunca produce un carry)
- ZF, SF y PF según el valor en EAX. (
a = a & amp; a = a-0
)
(Excepto por el AF obsoleto (indicador de transporte auxiliar, utilizado por las instrucciones ASCII / BCD). La PRUEBA lo deja sin definir , pero CMP lo establece " según el resultado " . Dado que restar cero no puede producir un acarreo desde el cuarto hasta el quinto bit, CMP siempre debe borrar el AF).
TEST es más pequeño (no inmediato) y, a veces, más rápido (se puede fusionar en una uop de comparación y ramificación en más CPU en más casos que en CMP). Eso hace que el test
el idioma preferido para probar un registro para cero o no .
La única razón común para usar CMP con un 0 inmediato es cuando desea comparar con un operando de memoria (por ejemplo, cmpb $ 0, (% esi)
para verificar un byte cero de terminación en el final de una cadena de estilo C de longitud implícita).
AVX512F agrega kortestw k1, k2
y AVX512DQ / BW (Skylake pero no KNL) agrega ktest / w / d / q k1, k2
, que operan en los registros de máscara AVX512 (k0..k7) pero aún establecen FLAGS regulares como test
, de la misma manera que El entero OR
o AND
lo hacen.
kortestw k1, k1
es la forma idiomática de derivar / cmovcc / setcc basado en un resultado de comparación AVX512, reemplazando SSE / AVX2 (v) pmovmskb / ps / pd
+ test
o cmp
.
El uso de jz
en lugar de je
puede ser confuso.
jz
y je
son literalmente los La misma instrucción , es decir, el mismo código de operación en el código de la máquina. Hacen lo mismo, pero tienen un significado semántico diferente para los humanos . Los desensambladores (y normalmente los resultados de los compiladores) solo usarán uno, por lo que se pierde la distinción semántica.
cmp
y sub
establecen ZF cuando sus dos entradas son iguales (es decir, el resultado de la resta es 0). je
(saltar si es igual) es el sinónimo semánticamente relevante.
test% eax,% eax
/ y% eax,% eax
nuevamente establece ZF cuando el resultado es cero, pero no hay una igualdad de " " prueba. ZF después de la prueba no te dice si los dos operandos eran iguales. Entonces jz
(saltar si es cero) es el sinónimo semánticamente relevante.
Este fragmento de código proviene de una subrutina que recibió un puntero a algo, probablemente alguna estructura u objeto. La segunda línea hace referencia a ese puntero y obtiene un valor de esa cosa, posiblemente un puntero o tal vez solo un int, almacenado como su segundo miembro (desplazamiento +4). Las líneas 3 y 4 prueban este valor en cero (NULL si es un puntero) y omite las siguientes operaciones (no se muestran) si es cero.
La prueba de cero a veces se codifica como una comparación con un valor cero literal inmediato, pero el compilador (¿o el humano?) que escribió esto podría haber pensado que una operación de prueba se ejecutaría más rápido, teniendo en cuenta todas las cosas modernas de la CPU como Canalización y cambio de nombre de registro. Es de la misma bolsa de trucos que contiene la idea de borrar un registro con XOR EAX, EAX (¡que vi en la matrícula de alguien en Colorado!) En lugar del MOV EAX obvio pero tal vez más lento, # 0 (uso una notación anterior) ).
En asm, como perl, TMTOWTDI.
Si eax es cero, realizará el salto condicional; de lo contrario, continuará con la ejecución en 319e9
En algunos programas se pueden usar para verificar un desbordamiento de búfer. En la parte superior del espacio asignado se coloca un 0. Después de ingresar los datos en la pila, busca el 0 al comienzo del espacio asignado para asegurarse de que el espacio asignado no esté desbordado.
Se usó en el ejercicio de ejercicios de exploits de la pila0 para comprobar si estaba desbordado y si no había y había un cero allí, se mostraría " Intentar nuevamente "
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
podríamos ver jg & # 65292; jle
Si testl% edx,% edx. jle .L3
podríamos encontrar fácilmente jle se adapta a (SF ^ OF) | ZF
, si% edx es cero, ZF = 1, pero si% edx no es cero y es -1, después de la prueba, la OF = 0 y la SF = 1, por lo que la bandera = verdadera, que implementa el salto
Lo siento, mi inglés es pobre