Я смотрел на какой-то вывод компилятора, и когда вызывается функция, обычно начинается настройка стека вызовов так:Компилятор генерировал неожиданные `IN AL, DX` (код операции` EC`) при настройке стека вызовов
PUSH EBP
MOV EBP, ESP
PUSH EDI
PUSH ESI
PUSH EBX
Таким образом, мы сохраняем базовый указатель вызывающей процедуры в стеке, перемещаем наш собственный указатель базы вверх, а затем сохраняем содержимое нескольких регистров в стеке. Затем они восстанавливаются до их первоначальных значений в конце процедуры, например:
LEA ESP, [EBP-0Ch]
POP EBX
POP ESI
POP EDI
POP EBP
RET
Пока что так хорошо. Однако я заметил, что в одной рутине код, устанавливающий стек вызовов, выглядит несколько иначе. Фактически это выглядит так:
IN AL, DX
PUSH EDI
PUSH ESI
PUSH EBX
Это довольно сбивает с толку по ряду причин. Во-первых, код конца кода идентичен приведенному выше для другого метода, и, в частности, ожидается, что сохраненная копия EBP будет доступна в стеке.
С другой стороны, если я правильно понял команда IN AL, DX
читает в AL
регистр, который является таким же, как EAX
регистр, и как это так бывает очень следующую команду здесь
XOR EAX, EAX
как программа хочет обнулить несколько вещей, которые он выделил в стеке.
Вопрос: Мне интересно, что здесь происходит, что я не понимаю. Код машины, переведенный как IN AL, DX
, представляет собой одиночный байт EC
, тогда как пара инструкций PUSH EBP MOV EBP, ESP будет соответствовать трем байтам 55 88 EC
. Разве дизассемблер неправильно это понимает? Или что-то полагается на побочный эффект, который я не понимаю?
Если кто-то любопытно, этот машинный код был сгенерирован JIT компилятором CLR, и я его просмотра с Visual Studio отладчик. Вот минимальное воспроизведение в C#:
class C {
string s = "";
public void f(string s) {
this.s = s;
}
}
Однако, обратите внимание, что это, как представляется, не детерминированной; иногда я, кажется, получаю версию IN AL, DX
, а в других случаях - PUSH EBP
, за которой следует MOV EBP, ESP
.
EDIT: Я начинаю сильно подозревать дизассемблера ошибка - я только что получил еще одну ситуацию, где он показывает IN AL, DX
(опкод EC
) и два предшествующих байтов в памяти 55 88
. Поэтому, возможно, дизассемблер просто путают о точке входа метода. (Хотя мне все еще хотелось бы понять, почему , что случилось!)
Спасибо, это отличная информация! –
Пила, что придет ;-) –