Открываем справку 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: да, баг есть (и я его видел на практике, хоть и не у себя).
спасибо, у меня сторонняя либа из-за этого глючила, если включить оптимизацию.
ОтветитьУдалитьА где гарантия, что EXCEPTION_PRIV_INSTRUCTION(#GP(0)?) портит только регистр EBX?
ОтветитьУдалитьМы то EBX - как раз не трогаем, и VMWare - в виду отсутствия - тоже...
Извиняюсь, в исходном коде пропустил xor ebx, ebx :(((
УдалитьУ меня такое ощущение, что код будет работать успешно всегда, потому что в процессе обработки исключения все регистры восстанавливаются. Иначе было бы так, что, например, вызов System.Move в блоке try..except валил бы программу (System.Move не использует фреймов), чего не наблюдается. Хотя почему System.Move? Любая чисто паскалевская функция/процедура без фреймов (но с сохранением/восстановлением регистров в стеке) может упасть по Access Violation. Имхо, тут что-то не то.
ОтветитьУдалитьВозможно. К сожалению, нет времени поглубже покопаться.
УдалитьС самой проблемой встретился давно на чужой машине, портились данные в регистрах, проблема была решена ручным сохранением/восстановлением. По давности - не могу подсказать по окружению (версия компилятора/настроек/etc.).
Сыровата задачка получилась...