由于更改 8087CW 模式而导致 System.Move 内存损坏 (png +stretchblt)
-
24-09-2019 - |
题
我有一个奇怪的内存损坏问题。经过几个小时的调试和尝试,我想我找到了一些东西。
例如:我做了一个简单的字符串赋值:
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,但不再使用)。
好吧,现在我发现 为什么 但不是 什么时候!
我改变了我的工作方式 汇编分析器 通过简单的检查(因此在进入和离开时检查所有功能):
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决赛:腐败!:
笔记2: 也许必须在 System.Move 中清除 FPU 堆栈?
解决方案
我还没有看到这个特定的问题,但是移动绝对可以搞的一团糟,如果FPU是在一个糟糕的状态。思科的VPN驱动程序可以搞砸可怕的,即使你没有做任何与网络相关的。
HTTP://brianorr.blogspot的.com / 2006/11 / Intel的奔腾d-浮点unit.html [破]
HTTPS://web.archive .ORG /网络/ 20160601043520 / HTTP://www.dankohn.com/archives/343
HTTP: //blog.excastle.com/2007/08/28/delphi-bug-of-the-day-fpu-stack-leak/ (由里奇Annand评论)
在我们的例子中,我们检测车司机VPN和换出移动和FillChar与德尔福7版本,具有Pascal版本替换IntToStr(Int64的版本使用FPU),并且,因为我们使用的FastMM,我们禁用其定制固定大小的移动程序也一样,因为他们甚至比System.Move更敏感。
其他提示
这可能是视频驱动程序中的错误,导致在执行 StretchBlt 操作时不保留 8087 控制字。
过去,我在使用某些打印机驱动程序时也看到过类似的行为。他们认为自己拥有 8087 CW,但他们错了......
请注意,Delphi 中 8087 CW 的默认值似乎是 1372 美元;有关 CW 值的更详细说明,请参见 本文: :它还解释了 Michael Justin 在他的 8087CW 被冲洗时所描述的情况。
——杰罗恩
只是为了您的信息(在某些情况下,人有同样的问题太):我们做了我们的软件升级的客户,以及完整的触摸屏锁定时,我们的应用程序启动!窗户被完全冻结!计算机必须重新启动(断电)。它花了一些时间找出完全冻结的原因。
幸运我们在FastMove.LargeSSEMove的AV之一(仅1!)堆栈跟踪。我禁用SSE中fastmove使用,问题也没有了。
顺便说:触摸屏具有VIA尼CPU与芯片组S3
您不仅可以使用FPU,也完全冻结时,得到的内存损坏!
对于那些仍然有兴趣在这个:还有另一种可能引起的问题:
德尔福里约仍然船只与一个破碎的先进电子版本的 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