В следующем ассемблерном коде, который я выгрузил с помощью objdump
:
lea 0x0(%esi,%eiz,1),%esi
Что такое регистр %eiz
? Что означает предыдущий код?
В следующем ассемблерном коде, который я выгрузил с помощью objdump
:
lea 0x0(%esi,%eiz,1),%esi
Что такое регистр %eiz
? Что означает предыдущий код?
См. Почему GCC LEA EIZ ?:
По-видимому,
%eiz
— это псевдорегистр, который всегда оценивается как ноль (например,r0
в MIPS).
...
В конце концов я нашел сообщение в списке рассылки от гуру binutils Яна Ланса Тейлора, в котором содержится ответ. Иногда GCC вставляет инструкции NOP в поток кода, чтобы обеспечить правильное выравнивание и тому подобное. Инструкция NOP занимает один байт, поэтому можно подумать, что можно просто добавить столько, сколько необходимо. Но, по словам Яна Лэнса Тейлора, чип быстрее выполняет одну длинную инструкцию, чем много коротких инструкций. Таким образом, вместо того, чтобы вставлять семь инструкций NOP, они используют одну причудливую LEA, которая использует семь байтов и семантически эквивалентна NOP.
(Очень поздно для игры, но это показалось интересным дополнением): это вовсе не регистр, это особенность кодировки инструкций Intel. При использовании байта ModRM для загрузки из памяти 3 бита используются для поля регистра для хранения 8 возможных регистров. Но место, где «был бы» ESP (указатель стека), вместо этого интерпретируется процессором как «за этой инструкцией следует байт SIB» (т. е. это расширенный режим адресации, а не ссылка на ESP). По причинам, известным только авторам, ассемблер GNU всегда представлял этот «ноль там, где в противном случае был бы регистр» как регистр «%eiz». Синтаксис Intel просто отбрасывает его.
(%esp)
/(%rsp)
вместо (%esp, %eiz, 1)
.
- person Peter Cordes; 30.06.2018
Энди Росс приводит гораздо больше основных рассуждений, но, к сожалению, ошибается или, по крайней мере, сбивает с толку технические детали. Это правда, что эффективный адрес, состоящий только из (%esp)
, не может быть закодирован только байтом ModR/M, поскольку вместо того, чтобы декодироваться как (%esp)
, он используется для обозначения того, что байт SIB также включен. Однако псевдорегистр %eiz
не всегда используется с байтом SIB для обозначения того, что байт SIB использовался.
Байт SIB (шкала/индекс/база) состоит из трех частей: индекс (регистр, такой как %eax
или %ecx
, к которому применяется шкала), шкала (степень двойки от 1 до 8, к которой относится регистр индекса). умножается на) и основание (еще один регистр, который добавляется к масштабированному индексу). Это то, что позволяет использовать такие инструкции, как add %al,(%ebx,%ecx,2)
(машинный код: 00 04 4b
-- код операции, modr/m, sib (обратите внимание, нет регистра %eiz, даже если использовался байт SIB)) (или в синтаксисе Intel «добавить BYTE PTR [ecx *2+ebx], al").
Однако %esp
нельзя использовать в качестве индексного регистра в байте SIB. Вместо того, чтобы разрешить эту опцию, Intel добавляет возможность использовать базовый регистр как есть без масштабирования или индексации. Поэтому для устранения неоднозначности между регистром add %al,(%ecx)
(машинный код: 00 01
-- код операции, modr/m) и add %al,(%ecx)
(машинный код: 00 04 21
-- код операции, modr/m, sib) вместо этого используется альтернативный синтаксис add %al,(%ecx,%eiz,1)
(или для Intel синтаксис: add BYTE PTR [ecx+eiz*1],al
).
И, как объясняется в статье, на которую ссылается Синан, эта конкретная инструкция (lea 0x0(%esi,%eiz,1),%esi
) просто используется как многобайтовая инструкция nop (эквивалентная esi = &*esi
), так что вместо нескольких инструкций nop должна выполняться только одна подобная nop инструкция.
(%esp)
является гораздо более полезным режимом адресации, чем (%esp, %esp, 1..8)
. Поскольку вы не можете кодировать base=ESP без байта SIB, вам нужно каким-то образом указать индекс. (Потому что ни для какой базы не требовался бы disp32, и они не хотели требовать disp32=0( , %esp, 1)
, чтобы сделать адресацию относительно ESP неоправданно дорогой.)
- person Peter Cordes; 30.06.2018