我对内存损坏问题很奇怪。经过几个小时的调试和尝试,我想我找到了一些东西。
例如:我做一个简单的字符串赋值:
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第2部分: FPU第3部分: FPU Final:腐败!:
注2:也许必须在System.Move中清除FPU堆栈?
答案 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)
对于那些仍然对此感兴趣的人:还有另一种可能的问题原因:
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