简体   繁体   English

第三方代码正在修改FPU控制字

[英]Third party code is modifying the FPU control word

The introduction - the long and boring part 介绍 - 漫长而无聊的部分

(The question is at the end) (问题在最后)

I am getting severe head aches over a third party COM component that keeps changing the FPU control word. 我正在对第三方COM组件产生严重的头痛,这些组件不断更改FPU控制字。

My development environment is Windows and Visual C++ 2008. The normal FPU control word specifies that no exceptions should be thrown during various conditions. 我的开发环境是Windows和Visual C ++ 2008.普通的FPU控制字指定在各种条件下不应抛出任何异常。 I have verified this with both looking at the _CW_DEFAULT macro found in float.h , as well as looking at the control word in the debugger at startup. 我已经通过查看float.h中的_CW_DEFAULT宏以及在启动时查看调试器中的控制字来验证这一点。

Everytime I make a call into the COM object, the control word is modified upon return. 每次我调用COM对象时,控制字在返回时被修改。 This is easy to defend against. 这很容易防御。 I simply reset the control word, and all is good. 我只是重置控制字,一切都很好。 The problem is when the COM component starts calling my event sink. 问题是当COM组件开始调用我的事件接收器时。 I can protect my code by reseting the control word as soon as I receive the event call, but I can't do anything as soon as I return from the event call. 我可以在收到事件调用后立即通过重置控制字来保护我的代码,但是一旦从事件调用返回,我就无法做任何事情。

I don't have the source for this COM component, but I am in contact with the author. 我没有此COM组件的源代码,但我与作者联系。 The responses I have had from him has been "Huh?". 我从他那里得到的回答是“嗯?”。 I don't think he has the slightest clue what I'm talking about, so I fear I have to do something about this myself. 我不认为他对我正在谈论的内容有任何线索,所以我担心自己必须对此做些什么。 I believe that his runtime (I think it's either Delphi or Borland C++, because the DLL is full of symbol names, all starting with capital T) , or some other third party code he's using, that's causing the problem. 我相信他的运行时(我认为它是Delphi或Borland C ++,因为DLL中充满了符号名称,都以大写字母T开头),或者他正在使用的其他一些第三方代码,这导致了问题。 I don't think his code explicitly modifies the FPU control word. 我不认为他的代码明确地修改了FPU控制字。

So, what can I do? 那么,我该怎么办? From a business point of view, it is imperative to use this third party component. 从业务角度来看,必须使用此第三方组件。 From a technical point of view, I could ditch it, and implement the communication's protocol myself. 从技术角度来看,我可以放弃它,并自己实现通信协议。 However, that would be really expensive, as this protocol involves handling credit card transactions. 然而,这将是非常昂贵的,因为该协议涉及处理信用卡交易。 We don't want to take on the liability. 我们不想承担责任。

I desperately need a hack-around, or some useful information about FPU settings in Borland products that I can pass along to the author of the component. 我迫切需要一个关于Borland产品中FPU设置的黑客或一些有用的信息,我可以传递给组件的作者。

The questions 问题

Is there anything I can do? 有什么能做的吗? I don't think the component author has what it takes to fix it (by judging from his rather clueless responses). 我不认为组件作者有什么需要修复它(通过他的相当无知的回答判断)。

I have been toying with the idea of installing my own exception handler, in which I just reset the control word in the handler, and tell Windows to continue executing. 我一直在想着安装自己的异常处理程序,我只是在处理程序中重置控制字,并告诉Windows继续执行。 I tried installing the handler with SetUnhandledExceptionFilter() , but for some reason, the exceptions are not caught. 我尝试使用SetUnhandledExceptionFilter()安装处理程序,但由于某种原因,不会捕获异常。

  1. Why aren't I catching the exceptions? 为什么我不抓住例外?
  2. If I succeed with catching FPU exceptions, resetting the FPU control word, and just let the execution continue as nothing has happened - are all bets off then? 如果我成功捕获FPU异常,重置FPU控制字,只是让执行继续,因为没有发生任何事情 - 所有赌注都关闭了吗?

Update 更新

I would like to thank everyone for their suggestions. 我想感谢大家的建议。 I have sent the author instructions on what he can do to make life easier for not just me, but many other clients of his code. 我已经向作者发送了关于他可以做些什么的说明,以便让我的生活变得更轻松,而不仅仅是我的代码的许多其他客户。 I suggested to him that he should sample the FPU control word at DllMain(DLL_PROCESS_ATTACH) , and save the control word for later, so that he can reset FPU CW before calling my event handlers, and returning from my calls. 我向他建议他应该在DllMain(DLL_PROCESS_ATTACH)对FPU控制字进行采样,并保存控制字以供日后使用,这样他就可以在调用我的事件处理程序之前重置FPU CW,并从我的调用中返回。

For now, I have a hack-around if anyone is interested. 就目前而言,如果有人有兴趣,我会有一个黑客攻击。 The hack-around is potentially a bad one, because I don't know what it'll do to his code. 黑客攻击可能是一个糟糕的,因为我不知道它对他的代码会做什么。 I have received confirmation earlier that he does not use any floating point numbers in his code, so this should be safe, barring some third party code he uses, that relies on FPU exceptions. 我之前收到的确认是他在代码中没有使用任何浮点数,所以这应该是安全的,除非他使用的某些第三方代码依赖于FPU异常。

The two modifications I have made to my app: 我对我的应用程序进行的两项修改:

  1. Wrap my message pump 包裹我的消息泵
  2. Install a window hook ( WH_CALLWNDPROC ) to catch the corner cases where the message pump is bypassed 安装窗口挂钩( WH_CALLWNDPROC )以捕获绕过消息泵的转角情况

In both instances, I check if the FPU CW has changed. 在这两种情况下,我都会检查FPU CW是否已更改。 If it has, I reset it to _CW_DEFAULT . 如果有,我将其重置为_CW_DEFAULT

I think your diagnosis that the component is written in an Embarcadero product is very likely to be true. 我认为您的诊断该组件是用Embarcadero产品编写的,很可能是真的。 Delphi's runtime library does indeed enable floating point exceptions, same for C++ Builder. Delphi的运行时库确实启用了浮点异常,对于C ++ Builder也是如此。

One of the nice things about Embarcaderos tools is that floating point errors get converted into language exceptions which makes numerical coding a lot easier. Embarcaderos工具的一个好处是浮点错误被转换为语言异常,这使得数字编码更容易。 That is going to be of little consolation to you! 这对你来说几乎没什么安慰!

This entire area is a colossal PITA. 整个区域都是庞大的PITA。 There are no rules whatsoever regarding the FP controls word. 关于FP控制字没有任何规则。 It's a total free-for-all. 这是一个完全免费的。

I don't believe that catching unhandled exceptions isn't going to get the job done because the MS C++ runtime will presumably already be catching these exceptions, but I'm no expert in that area and I may be wrong. 我不相信捕获未处理的异常不会完成任务,因为MS C ++运行时可能已经捕获了这些异常,但我不是那个领域的专家,我可能错了。

I believe that your only realistic solution is to set the FPU to what you want it to be whenever execution arrives in your code, and restore it when execution leaves your code. 我相信您唯一可行的解​​决方案是将FPU设置为执行到达代码时所需的FPU,并在执行离开代码时将其还原。 I don't know enough about COM event sinks to understand why they present an obstacle to doing this. 我不太了解COM事件接收器,以了解它们为什么会出现这样做的障碍。

My product includes a DLL implemented in Delphi and I suffer from the reverse problem. 我的产品包含一个用Delphi实现的DLL,我遇到了相反的问题。 Mostly the clients that call in have an FPU control word that disables exceptions. 通常,调用的客户端具有禁用异常的FPU控制字。 The strategy we adopt is to remember the 8087CW on entry, set it to the standard Delphi CW before executing code, and then restore it at the exit point. 我们采用的策略是在进入时记住8087CW,在执行代码之前将其设置为标准Delphi CW,然后在出口点恢复它。 We take care to deal with callbacks too by restoring the caller's 8087CW before making the callback. 在进行回调之前,我们还会通过恢复来电者的8087CW来处理回调问题。 This is a plain DLL rather than a COM object so it's probably a bit simpler. 这是一个普通的DLL而不是COM对象,因此它可能更简单一些。

If you decide to attempt to get the COM supplier to modify their code then they need to call the Set8087CW() function. 如果您决定尝试让COM供应商修改他们的代码,那么他们需要调用Set8087CW()函数。

However, since there are no rules to the game, I believe that the COM object vendor would be justified in refusing to change their code and to put the onus back on you. 但是,由于游戏没有规则,我相信COM对象供应商在拒绝更改代码并将责任推回给您时是合理的。

Sorry if this is not a 100% conclusive answer, but I couldn't get all these thoughts into a comment! 对不起,如果这不是一个100%的结论性答案,但我无法将所有这些想法发表评论!

Although FP control word is per-thread, dllmain functions are called when new threads are created, i don't think you can avoid this short of going to a new process. 虽然FP控制字是每线程的,但是在创建新线程时会调用dllmain函数,我认为你不能避免这种新的进程。

I suggest you spin-off a new process to run the COM and chat with the process with your favorite inter-process communication method (eg windows message, out-of-proc COM, named pipe, socket, etc). 我建议您分拆一个新进程来运行COM并使用您最喜欢的进程间通信方法(例如Windows消息,进程外COM,命名管道,套接字等)与进程聊天。 In this way the COM server is free to do all kinds of damage (including crashing itself) without bring your host process down. 通过这种方式,COM服务器可以自由地进行各种损坏(包括崩溃),而不会导致主机进程崩溃。

Another idea is to write a DLL whose sole purpose is to reset the FPU in its DllMain and load it immediately after the offending DLL. 另一个想法是编写一个DLL,其唯一目的是重置其DllMain中的FPU并在违规DLL之后立即加载它。 Windows is probably using the loading order to call DllMain when new threads are created, including threads created by the COM server. Windows可能正在使用加载顺序在创建新线程时调用DllMain,包括COM服务器创建的线程。 Note this depends on Windows's internal behavior. 请注意,这取决于Windows的内部行为。 Also the COM server may actually depends on fp exceptions since it enables them. 此外,COM服务器实际上可能依赖于fp异常,因为它启用了它们。 Disabling FP exceptions may causes the COM server to behave unexpectedly. 禁用FP异常可能会导致COM服务器意外运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM