Повреждение памяти в System.Move из-за изменения режима 8087CW (png + stretchblt)

У меня странная проблема с повреждением памяти. После многих часов отладки и попыток, я думаю, что-то нашел.

Например: я делаю простое строковое присваивание:

sTest := 'SET LOCK_TIMEOUT ';

Однако результатом иногда становится:

sTest = 'SET LOCK'#0'TIMEOUT '

Таким образом, _ заменяется на 0 байт.

Я видел это однажды (воспроизведение сложно, зависит от времени) в функции System.Move, когда она использует стек FPU (fild, fistp) для быстрого копирования памяти (в случае перемещения от 9 до 32 байт):

...
@@SmallMove: {9..32 Byte Move}
fild    qword ptr [eax+ecx] {Load Last 8}
fild    qword ptr [eax] {Load First 8}
cmp     ecx, 8
jle     @@Small16
fild    qword ptr [eax+8] {Load Second 8}
cmp     ecx, 16
jle     @@Small24
fild    qword ptr [eax+16] {Load Third 8}
fistp   qword ptr [edx+16] {Save Third 8}
...

Используя представление FPU и 2 представления отладки памяти (Delphi -> View -> Debug -> CPU -> Memory), я увидел, что все идет не так ... однажды ... однако воспроизвести не удалось ...

Этим утром я прочитал кое-что о режиме 8087CW, и да, если это изменится на $ 27F, я получу повреждение памяти! Обычно это $ 133F:

Разница между $ 133F и $ 027F заключается в том, что $ 027F устанавливает FPU для выполнения менее точных вычислений (ограничение Double вместо вместо Extended) и другой обработки бесконечности (которая использовалась для более старых FPU, но больше не используется).

Хорошо, теперь я нашелЗачем но неткогда!

Я изменил работу моегоAsmProfiler с простой проверкой (поэтому все функции проверяются при входе и выходе):

if Get8087CW = $27F then    //normally $1372?
  if MainThreadID = GetCurrentThreadId then  //only check mainthread
    DebugBreak;

Я «профилировал» некоторые юниты, DLL и бинго (см. Стек):

Windows.StretchBlt(3372289943,0,0,514,345,4211154027,0,0,514,345,13369376)
pngimage.TPNGObject.DrawPartialTrans(4211154027,(0, 0, 514, 345, (0, 0), (514, 345)))
pngimage.TPNGObject.Draw($7FF62450,(0, 0, 514, 345, (0, 0), (514, 345)))
Graphics.TCanvas.StretchDraw((0, 0, 514, 345, (0, 0), (514, 345)),$7FECF3D0)
ExtCtrls.TImage.Paint
Controls.TGraphicControl.WMPaint((15, 4211154027, 0, 0))

Так происходит в StretchBlt ...

Что делать сейчас? Это вина Windows или ошибка PNG (включена в D2007)? Или функция System.Move не безопасна?

Замечания: просто попытка воспроизвести не работает

  Set8087CW($27F);
  sSQL := 'SET LOCK_TIMEOUT ';

Это кажется более экзотичным ... Но с помощью отладки на 'Get8087CW = $ 27F' я мог бы воспроизвести его на другой строке: FPU part 1: ФПУ часть 2: ФПУ часть 3: ФПУ Финал: коррумпирован!

Заметка 2: Может быть, стек FPU должен быть очищен в System.Move?

Ответы на вопрос(4)

Ваш ответ на вопрос