由于更改了8087CW模式(Png + stretchblt),System.Move中的内存损坏

时间:2010-04-06 12:11:10

标签: delphi png stretchblt x87

我对内存损坏问题很奇怪。经过几个小时的调试和尝试,我想我找到了一些东西。

例如:我做一个简单的字符串赋值:

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)和不同的infiniti处理(用于较旧的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'的debugbreak,我可以在另一个字符串上重现它: FPU第1部分: FPU part 1 FPU第2部分: FPU part 2 FPU第3部分: FPU part 3 FPU Final:腐败!: FPU Final: corrupt!

注2:也许必须在System.Move中清除FPU堆栈?

4 个答案:

答案 0 :(得分:9)

我还没有看到这个特殊问题,但如果FPU处于不良状态,Move肯定会搞砸。即使您没有做任何与网络相关的事情,思科的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/(Ritchie Annand的评论)

在我们的例子中,我们检测到有缺陷的VPN驱动程序,并将Move和FillChar替换为Delphi 7版本,将IntToStr替换为Pascal版本(Int64版本使用FPU),并且,由于我们使用的是FastMM,因此我们禁用它的自定义固定大小移动例程也是如此,因为它们比System.Move更容易受到影响。

答案 1 :(得分:3)

视频驱动程序中的错误可能是在执行StretchBlt操作时不保留8087控制字。 在过去,我在使用某些打印机驱动程序时看到了类似的行为。他们认为他们拥有8087 CW并且错了......

注意Delphi中8087 CW的默认值似乎是1372美元;有关CW值的更详细解释,请参阅this article:它还解释了Michael Justin在他的8087CW被冲洗时所描述的情况。

- 的Jeroen

答案 2 :(得分:2)

仅供参考(如果其他人也有同样的问题):我们为客户升级了我们的软件,并在我们的应用程序启动时锁定了完整的触摸屏! Windows完全冻结了!电脑必须重新启动(关机)。花了一些时间才弄清楚完全冻结的原因。

幸运的是,我们在FastMove.LargeSSEMove中有一个(仅1个)AV堆栈跟踪。我在fastmove中禁用了SSE的使用,问题就消失了。

顺便说一句:触摸屏有一个带有S3芯片组的威盛Nehemiah cpu。

因此,不仅可以在使用FPU时获得内存损坏,还可以完全冻结!

答案 3 :(得分:1)

对于那些仍然对此感兴趣的人:还有另一种可能的问题原因:

Delphi Rio仍随附Move的ASM版本损坏。

我很高兴今天遇到了这个错误,幸运的是,我有一个可重现的测试用例。问题是这段代码:

* ***** 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's 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

参考