5 марта 2016 г.

Ответ на задачку №21

Ответ на задачку №21.

Открываем справку Delphi на разделе "Program Control", подразделе "Register saving conventions":
Procedures and functions must preserve the EBX, ESI, EDI, and EBP registers, but can modify the EAX, EDX, and ECX registers. When implementing a constructor or destructor in assembler, be sure to preserve the DL register.

Хмм, но вроде бы мы не трогаем ESI, EDI и EBP, а EBX - трогаем, но мы же его сохраняем и восстанавливаем?

Да, но только в случае если мы запущены под VMWare (в этом случае мы идём по нормальному пути выполнения). Если же мы не запущены под VMWare, то инструкция in возбудит исключение, что приведёт к прыжку на блок try/except - следовательно, восстановление регистра EBX будет пропущено.

Вот вам и рецепт для катастрофы. Если до вызова этого кода программа сохранит в EBX что-то важное, то это значение будет потеряно/перезаписано. Таким образом, этот код будет работать успешно только в случае, если в регистре EBX будет мусор (т.е. он не используется).

Иными словами, вылетит программа или нет - целиком зависит от компилятора и того, как он скомпилирует код. Баг может происходить, а может и не происходить в зависимости от настроек компилятора (Stack Frames, Optimization), окружающего кода, а также платформы (x32/x64) и фазы луны. Правильный же код сохранял/восстанавливал бы EBX не внутри, а снаружи блока try/except.

TL;DR: да, баг есть (и я его видел на практике, хоть и не у себя).

5 комментариев:

  1. спасибо, у меня сторонняя либа из-за этого глючила, если включить оптимизацию.

    ОтветитьУдалить
  2. А где гарантия, что EXCEPTION_PRIV_INSTRUCTION(#GP(0)?) портит только регистр EBX?
    Мы то EBX - как раз не трогаем, и VMWare - в виду отсутствия - тоже...

    ОтветитьУдалить
    Ответы
    1. Извиняюсь, в исходном коде пропустил xor ebx, ebx :(((

      Удалить
  3. У меня такое ощущение, что код будет работать успешно всегда, потому что в процессе обработки исключения все регистры восстанавливаются. Иначе было бы так, что, например, вызов System.Move в блоке try..except валил бы программу (System.Move не использует фреймов), чего не наблюдается. Хотя почему System.Move? Любая чисто паскалевская функция/процедура без фреймов (но с сохранением/восстановлением регистров в стеке) может упасть по Access Violation. Имхо, тут что-то не то.

    ОтветитьУдалить
    Ответы
    1. Возможно. К сожалению, нет времени поглубже покопаться.

      С самой проблемой встретился давно на чужой машине, портились данные в регистрах, проблема была решена ручным сохранением/восстановлением. По давности - не могу подсказать по окружению (версия компилятора/настроек/etc.).

      Сыровата задачка получилась...

      Удалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.