2017-02-17 17 views
4

Я смотрел на какой-то вывод компилятора, и когда вызывается функция, обычно начинается настройка стека вызовов так:Компилятор генерировал неожиданные `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. Поэтому, возможно, дизассемблер просто путают о точке входа метода. (Хотя мне все еще хотелось бы понять, почему , что случилось!)

ответ

7

Похоже, вы используете VS2015. Ваш вывод верен, его отладочный движок содержит лот ошибок. Да, неправильный адрес. Не единственная проблема, она не восстанавливает контрольные точки должным образом, и вы склонны видеть инструкцию INT3 в коде.И он не может правильно обновить разборку, когда дрожание перегенерировало код и заменило вызовы-заглушки. Вы не можете доверять всему, что видите.

Я рекомендую использовать Инструменты> Параметры> Отладка> Общие и установите флажок «Использовать управляемый режим совместимости». Это заставляет отладчик использовать старый отладочный движок VS2010. Он намного более стабилен.

Вы потеряете некоторые функции с этим движком, такие как проверка возвращаемого значения и 64-битное Edit + Continue. Не будет пропущена, когда вы выполните такую ​​отладку. Однако вы увидите поддельные кодовые адреса, как это всегда было раньше, поэтому все CALL-адреса ошибочны, и вы не можете легко идентифицировать вызовы в CLR. Перевертывание двигателя назад и вперед - это своеобразное решение, но, конечно, большая досада.

Это тоже не сработало, я не видел улучшений в Обновлениях. Но у них, без сомнения, был большой список ошибок для работы, VS2015 отправлен до того, как это было сделано. Надеюсь, VS2017 лучше, мы скоро узнаем.

+0

Спасибо, это отличная информация! –

+0

Пила, что придет ;-) –