`testl` eax против eax?
Я пытаюсь понять некоторые сборки.
Сборка следующая, меня интересует testl
линия:
000319df 8b4508 movl 0x08(%ebp), %eax
000319e2 8b4004 movl 0x04(%eax), %eax
000319e5 85c0 testl %eax, %eax
000319e7 7407 je 0x000319f0
Я пытаюсь понять эту точку testl
между %eax
а также %eax
? Я думаю, что специфика того, что этот код не важен, я просто пытаюсь понять тест с самим собой - не всегда ли значение будет правдой?
8 ответов
Это проверяет, eax
0 или выше или ниже. В этом случае скачок берется, если eax
это 0.
Значение test
это И аргументы вместе, и проверить результат на ноль. Так что этот код проверяет, равен ли EAX нулю или нет. je
будет прыгать, если ноль.
Кстати, это генерирует меньшую инструкцию, чем cmp eax, 0
что является причиной того, что компиляторы обычно делают это таким образом.
Тестовая команда выполняет логическую операцию И между операндами, но не записывает результат обратно в регистр. Только флаги обновляются.
В вашем примере тест eax, eax установит флаг нуля, если eax равен нулю, флаг знака, если установлен старший бит, а также некоторые другие флаги.
Инструкция Jump if Equal (je) переходит, если установлен нулевой флаг.
Вы можете перевести код в более читаемый код, например так:
cmp eax, 0
je somewhere
Это имеет ту же функциональность, но требует больше байтов кода. Вот почему компилятор отправил тест вместо сравнения.
test
как and
, за исключением того, что он записывает только флаги, оставляя оба своих ввода неизмененными. С двумя разными входами полезно проверять, все ли биты равны нулю, или если установлен хотя бы один бит. (например test al, 3
устанавливает ZF, если EAX кратно 4 (и поэтому оба его младших 2 бита обнуляются).
test eax,eax
устанавливает все флаги точно так же, как cmp eax, 0
будет:
- CF и OF очищены (AND/TEST всегда так делает; вычитание нуля никогда не приводит к переносу)
- ZF, SF и PF в соответствии со значением в EAX. (
a = a&a = a-0
)
(За исключением устаревшего AF (флаг вспомогательного переноса, используемый командами ASCII/BCD). TEST оставляет его неопределенным, но CMP устанавливает его "в соответствии с результатом". Так как вычитание нуля не может произвести перенос с 4-го по 5-й немного, CMP всегда должен очищать AF).
TEST меньше (не немедленный), а иногда и быстрее (может слиться воедино в мопе сравнения и ветвления на большем количестве процессоров в большем количестве случаев, чем CMP). Что делает test
предпочтительная идиома для проверки регистра на ноль или нет.
Единственная общая причина использования CMP с немедленным 0 - это когда вы хотите сравнить с операндом памяти (например, cmpb $0, (%esi)
проверить наличие завершающего нулевого байта в конце строки C-стиля неявной длины).
AVX512F добавляет kortestw k1, k2
и AVX512DQ/BW (Skylake, но не KNL) добавить ktestb/w/d/q k1, k2
, которые работают с регистрами маски AVX512 (k0..k7), но все еще устанавливают обычные флаги как test
делает, так же, как целое число OR
или же AND
инструкции делают.
kortestw k1,k1
идиоматический способ ветвления / cmovcc / setcc на основе результата сравнения AVX512, заменяющий SSE / AVX2 (v)pmovmskb/ps/pd
+ test
или же cmp
,
Использование jz
против je
может быть запутанным
jz
а также je
буквально одна и та же инструкция, т.е. один и тот же код операции в машинном коде. Они делают то же самое, но имеют различное смысловое значение для людей. Дизассемблеры (и, как правило, выходные данные asm из компиляторов) будут использовать только один, поэтому семантическое различие потеряно.
cmp
а также sub
установите ZF, когда их два входа равны (т.е. результат вычитания равен 0). je
(переход, если равен) семантически значимый синоним.
test %eax,%eax
/ and %eax,%eax
снова устанавливает ZF, когда результат равен нулю, но нет теста на "равенство". ZF после теста не говорит вам, были ли два операнда равны. Так jz
(переход на ноль) - семантически значимый синоним.
Этот фрагмент кода взят из подпрограммы, которой был дан указатель на что-то, возможно, некоторую структуру или объект. Вторая строка разыменовывает этот указатель, извлекая значение из этой вещи - возможно, самого указателя или, может быть, просто int, сохраненного как его 2-й член (смещение +4). 3-я и 4-я строки проверяют это значение на ноль (NULL, если это указатель) и пропускают следующие несколько операций (не показаны), если оно равно нулю.
Тест на ноль иногда кодируется как сравнение с непосредственным литеральным нулевым значением, но компилятор (или человек?), Который написал это, мог подумать, что тестовая операция будет выполняться быстрее - принимая во внимание все современные вещи ЦП, такие как конвейерная обработка и регистр. переименование. Это из того же пакета трюков, в котором содержится идея очистки регистра с помощью XOR EAX,EAX (который я видел на чьем-то номерном знаке в Колорадо!), А не очевидного, но, возможно, более медленного MOV EAX, #0 (я использую более старую запись).
В asm, как в perl, TMTOWTDI.
Если eax равен нулю, он выполнит условный переход, в противном случае он продолжит выполнение на 319e9.
В некоторых программах их можно использовать для проверки переполнения буфера. В самой верхней части выделенного пространства помещается 0. После ввода данных в стек он ищет 0 в самом начале выделенного пространства, чтобы убедиться, что выделенное пространство не переполнено.
Он использовался в упражнении эксплойтов stack0 для проверки, переполнен ли он, и если его там нет и был ноль, на экране отобразилось бы "Попробуй еще раз"
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
Мы могли бы увидеть jg,jle, если testl %edx,%edx. jle .L3
мы могли бы легко найти JLE это костюм (SF^OF)|ZF
, если%edx равен нулю,ZF=1, но если%edx не равен нулю и равен -1, после testl, OF=0 и SF =1, поэтому флаг =true, который реализует переход.sorry, мой английский оставляет желать лучшего