Подтвердить что ты не робот

Повреждение памяти в 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 и двух отладочных представлений памяти (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 и bingo (см. стек):

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 ';

Кажется, это более экзотично... Но по debugbreak на 'Get8087CW = $27F' я мог воспроизвести его на другой строке: FPU часть 1: FPU part 1 FPU часть 2: FPU part 2 FPU часть 3: FPU part 3 FPU Final: коррумпирован!: FPU Final: corrupt!

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

4b9b3361

Ответ 1

Я не видел этой конкретной проблемы, но Move может определенно испортиться, если FPU находится в плохом состоянии. Драйвер Cisco VPN может ужасно испортить ситуацию, даже если вы ничего не делаете с сетью.

http://brianorr.blogspot.com/2006/11/intel-pentium-d-floating-point-unit.html [сломано]

https://web.archive.org/web/20160601043520/http://www.dankohn.com/archives/343

http://blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/ (комментарии Ричи Аннана)

В нашем случае мы обнаруживаем неисправный драйвер VPN и заменяем Move и FillChar версиями Delphi 7, заменяем IntToStr версией Pascal (Int64-версия использует FPU), и, поскольку мы используем FastMM, мы отключаем его пользовательское исправленное процедуры перемещения по размеру тоже, так как они даже более восприимчивы, чем System.Move.

Ответ 2

Это может быть ошибка в вашем видеодрайвере, который не сохраняет управляющее слово 8087, когда он выполняет операцию StretchBlt.
Раньше я видел подобное поведение при использовании некоторых драйверов принтера. Они считают, что они владеют 8087 CW и ошибаются...

Обратите внимание, что значение по умолчанию 8087 CW в Delphi кажется $1372; для более подробного объяснения значений CW см. в этой статье: это также объясняет ситуацию, о которой Майкл Джастин описал, когда его 8087CW получил hosed.

- Йерун

Ответ 3

Просто для вашей информации (в случае, если у какой-то другой проблемы есть такая же проблема): мы сделали обновление нашего программного обеспечения для клиента, а полный сенсорный экран заблокирован, когда наше приложение было запущено! Окна были полностью заморожены! ПК пришлось перезапустить (отключить питание). Потребовалось некоторое время, чтобы выяснить причину полного замораживания.

К счастью, в FastMove.LargeSSEMove у нас была одна (всего 1!) стоп-кадр AV. Я отключил использование SSE в fastmove, и проблема исчезла.

Кстати, сенсорный экран имеет процессор VIA Nehemiah с чипсетом S3.

Таким образом, вы можете не только повредить память при использовании FPU, но и полностью заморозить!

Ответ 4

Для тех, кто все еще заинтересован в этом: Есть еще одна возможная причина проблем:

Delphi Rio по-прежнему поставляется с сломанной ASM-версией Move.

Я имел удовольствие столкнуться с этой ошибкой сегодня, к счастью, у меня был воспроизводимый контрольный пример. Проблема заключается в следующем фрагменте кода:

* ***** BEGIN LICENSE BLOCK *****
 *
 * The assembly function Move is licensed under the CodeGear license terms.
 *
 * The initial developer of the original code is Fastcode
 *
 * Portions created by the initial developer are Copyright (C) 2002-2004
 * the initial developer. All Rights Reserved.
 *
 * Contributor(s): John O'Harrow
 *
 * ***** END LICENSE BLOCK ***** *)

// ... some less interesting parts omitted ...

@@LargeMove:
        JNG     @@LargeDone {Count < 0}
        CMP     EAX, EDX
        JA      @@LargeForwardMove

        // the following overlap test is broken
        // when size>uint(destaddr), EDX underflows to $FFxxxxxx, in which case 
        // we jump to @LargeForwardMove even if a backward loop would be appropriate
        // this will effectively shred everything at EDX + size
        SUB     EDX, ECX              // when this underflows ...
        CMP     EAX, EDX              // ... we also get CF=1 here (EDX is usually < $FFxxxxxx)
        LEA     EDX, [EDX+ECX]        // (does not affect flags)
        JNA     @@LargeForwardMove    // ... CF=1 so let jump into disaster!

        SUB     ECX, 8 {Backward Move}
        PUSH    ECX
        FILD    QWORD PTR [EAX+ECX] {Last 8}
        FILD    QWORD PTR [EAX] {First 8}
        ADD     ECX, EDX
        AND     ECX, -8 {8-Byte Align Writes}
        SUB     ECX, EDX

Рекомендации