ИЛИ инструкция по сборке в регистр ECX

В книге, которую я читаю, нам дан следующий фрагмент и проблема:

Эта функция использует комбинацию SCAS и STOS для выполнения своей работы. Сначала объясните, каков тип [EBP+8] и [EBP+C] в строках 1 и 8 соответственно. Затем объясните, что делает этот фрагмент:

01: 8B 7D 08    mov edi, [ebp+8]
02: 8B D7       mov edx, edi
03: 33 C0       xor eax, eax
04: 83 C9 FF    or ecx, 0FFFFFFFFh
05: F2 AE       repne scasb
06: 83 C1 02    add ecx, 2
07: F7 D9       neg ecx
08: 8A 45 0C    mov al, [ebp+0Ch]
09: 8B AA       mov edi, edx
10: F3 AA       rep stosb
11: 8B C2       mov eax, edx

Я почти все выяснил после проверки с помощью онлайн-решения ( https://johannesbader.ch/2014/05/practical-reverse-engineering-exercises-page-11/), однако один шаг в этом фрагменте все еще не делает смысл от меня.

Согласно онлайн-решению, когда мы запускаем команду or ecx, 0FFFFFFFFh в строке 4 написано

Мы [сейчас] интерпретируем ECX как целое число со знаком -1

Чтобы узнать, каков будет результат для or команда, не нужно ли нам знать заранее, какова ценность ECX является? И почему значение -1?

Спасибо

2 ответа

Решение

32-битное два дополнения представления -1 является 0xFFFFFFFF (все-оны). 1 OR x всегда 1 так что это безусловно устанавливает ecx до -1. Этот трюк работает только для -1, потому что OR может только устанавливать биты, но не сбрасывать их в ноль.


Та часть решения, которую вы цитируете, об интерпретации " ecx как целое число со знаком -1", имеет смысл только в контексте следующей команды gdb: (gdb) p/d $ecx -> $7 = -1,

rep префиксы обрабатывают ecx как неподписанный счетчик. Установка ecx в -1 / UINT_MAX означает repne scasb остановится только когда найдет ноль в памяти, а не потому что ecx отсчитывать до конца (Теоретически, если бы не было нуля, он бы отсчитывал и заканчивал таким образом, но на практике он вначале был бы segfault. -1 не особый случай для rep).


Зачем использовать or: размер кода

"Нормальный" способ установить регистр в любое значение, отличное от нуля, - это 5 байтов mov r32, imm32 Инсн, например B9 FF FF FF FF mov ecx,-1,

Если вам важнее размер кода, чем скорость, или вы знаете, что ложная зависимость от ecx здесь не проблема, вы можете сохранить два байта, используя 8-битный немедленный знак: or r/m32, imm8,

83 C9 FF    or ecx, 0FFFFFFFFh

Ни один из битов в результате фактически не зависит от старого значения ecx, потому что. Тем не менее, реальные процессоры этого не делают, поэтому выполнение не по порядку не может начаться до ecx готов. Это ложная зависимость от старого значения ecx. mov ломает зависимость от предыдущего значения. (Подробнее об этом см. Вики-тэг x86, особенно руководства Agner Fog).

or ecx, imm8 нужен байт ModRM для кодирования места назначения как ecx, в отличие от этой формы mov где есть отдельный код операции для каждого регистра назначения. Там, к сожалению, нет кода операции для mov r/m32, imm8, что позволило бы сохранить 2 байта кода во многих инструкциях.

Если бы Intel хотела отказаться от обратной совместимости с недокументированными инструкциями, они могли бы добавить ее. (8086 его не было, потому что он помог бы только 16-битному коду при перемещении немедленного в память. Они уже выделили 8 кодов операций для mov r16, imm16, что составляет 3 байта в 16-битном режиме, где не требуется префикс размера операнда, как несуществующий mov r/m16, imm8 было бы.)


Так что это полезная идиома для оптимизации размера кода, например, для загрузчика, или ответа машинного кода на https://codegolf.stackexchange.com/. (Да, это вещь.)

Другой связанный трюк использует 3-байтовый lea создать константу, если у вас уже есть другая константа в другом регистре. например для x86-64 Adler32, мне нужно было два обнуленных регистра и 1 так я использовал

401120:       31 c0          xor  eax,eax
401122:       99             cdq                 # zero rdx by sign-extending eax (0) into edx
401123:       8d 7a 01       lea  edi,[rdx+0x1]  # edi=0+1, using a reg + disp8 addressing mode

F в шестнадцатеричном виде 1111 в двоичном, так 0ffffffffh является DWORD со всеми битами, установленными в 1, Согласно таблице истинности OR, если ты OR что-нибудь с 1, ты все еще получаешь 1, Так что ни на что ECX ранее состоявшийся, после этого конкретного OR операция все его биты будут установлены в 1, который 0ffffffffh, Это объясняется в первой ссылке, предоставленной Жозе Мануэлем Абаркой Родригесом в комментарии.

Архитектура Intel (так же как и большинство аппаратных архитектур) использует так называемое "представление двух дополнений" для установления соответствия между числами и битовыми комбинациями в регистрах или в памяти. В двух дополнительных представлениях -1 представляется как 0ffffffffh, Это объясняется во второй ссылке, предоставленной Жозе Мануэлем Абаркой Родригесом в комментарии.

Другие вопросы по тегам