25

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

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

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 模式的内容,是的,如果将其更改为 $27FI,则会导致内存损坏!通常是 $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 part 1: FPU 第 1 部分 FPU part 2: FPU 第 2 部分 FPU part 3: FPU 第 3 部分 FPU Final:损坏!: FPU决赛:腐败!

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

4

4 回答 4

10

我还没有看到这个特殊的问题,但是如果 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 驱动程序并用 Delphi 7 版本替换 Move 和 FillChar,用 Pascal 版本替换 IntToStr(Int64 版本使用 FPU),并且由于我们使用的是 FastMM,我们禁用了它的自定义修复大小移动例程也是如此,因为它们比 System.Move 更容易受到影响。

于 2010-04-06T15:08:19.833 回答
3

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

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

——杰伦

于 2010-04-06T14:43:59.660 回答
3

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

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

参考

于 2018-12-12T14:01:46.153 回答
2

仅供参考(以防其他人也有同样的问题):我们为客户升级了我们的软件,并且在我们的应用程序启动时整个触摸屏都被锁定了!窗户被完全冻结了!必须重新启动电脑(关闭电源)。花了一些时间找出完全冻结的原因。

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

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

因此,使用 FPU 时不仅会出现内存损坏,而且还会完全冻结!

于 2011-06-07T12:55:20.593 回答