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

发布于 2024-11-27 16:24:16 字数 1480 浏览 0 评论 0原文

简介 - 又长又无聊的部分

(问题在最后)

我对不断更改 FPU 控制字的第三方 COM 组件感到非常头疼。

我的开发环境是Windows和Visual C++ 2008。正常的FPU控制字指定在各种情况下不应抛出异常。我通过查看 float.h 中的 _CW_DEFAULT 宏以及启动时调试器中的控制字来验证这一点。

每次我调用 COM 对象时,控制字都会在返回时被修改。这很容易防御。我只是重置了控制字,一切都很好。问题是当 COM 组件开始调用我的事件接收器时。我可以通过在收到事件调用后立即重置控制字来保护我的代码,但从事件调用返回后我无法立即执行任何操作。

我没有这个 COM 组件的源代码,但我正在与作者联系。我从他那里得到的回应是“嗯?”。我认为他对我在说什么一无所知,所以我担心我必须自己做点什么。我相信他的运行时(我认为是 Delphi 或 Borland C++,因为 DLL 充满了符号名称,全部以大写 T 开头),或者他正在使用的其他第三方代码,导致了问题。我不认为他的代码显式修改了 FPU 控制字。

那么,我能做什么呢?从商业角度来看,使用这个第三方组件势在必行。从技术角度来看,我可以放弃它,自己实现通信协议。然而,这将非常昂贵,因为该协议涉及处理信用卡交易。我们不想承担责任。

我迫切需要一种破解方法,或者一些有关 Borland 产品中 FPU 设置的有用信息,我可以将这些信息传递给组件的作者。

问题

我能做些什么吗?我认为组件作者没有能力修复它(从他相当无能的回答来看)。

我一直在考虑安装自己的异常处理程序,其中我只是重置处理程序中的控制字,并告诉 Windows 继续执行。我尝试使用 SetUnhandledExceptionFilter() 安装处理程序,但由于某种原因,未捕获异常。

  1. 为什么我没有捕获异常?
  2. 如果我成功捕获 FPU 异常,重置 FPU 控制字,然后让执行继续,就像什么都没有发生一样 - 那么所有的赌注都结束了吗?

更新

我要感谢大家的建议。我已向作者发送了说明,说明他可以采取哪些措施来让我以及他的代码的许多其他客户的生活变得更轻松。我建议他应该在 DllMain(DLL_PROCESS_ATTACH) 处对 FPU 控制字进行采样,并保存该控制字供以后使用,以便他可以在调用我的事件处理程序并从我的事件处理程序返回之前重置 FPU CW。来电。

现在,如果有人感兴趣的话,我有一个解决办法。这种黑客攻击可能是一件坏事,因为我不知道它会对他的代码产生什么影响。我之前已经收到确认,他在代码中没有使用任何浮点数,所以这应该是安全的,除非他使用一些依赖于 FPU 异常的第三方代码。

我对我的应用程序进行了两项修改:

  1. 包装消息泵
  2. 安装窗口挂钩 (WH_CALLWNDPROC) 以捕获消息泵被绕过的极端情况

在这两种情况下,我都会检查 FPU CW 是否具有改变了。如果有,我将其重置为 _CW_DEFAULT

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.

My development environment is Windows and Visual C++ 2008. The normal FPU control word specifies that no exceptions should be thrown during various conditions. 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.

Everytime I make a call into the COM object, the control word is modified upon return. 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. 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. 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. I don't think his code explicitly modifies the FPU control word.

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.

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. I tried installing the handler with SetUnhandledExceptionFilter(), but for some reason, the exceptions are not caught.

  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?

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.

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.

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

In both instances, I check if the FPU CW has changed. If it has, I reset it to _CW_DEFAULT.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

琴流音 2024-12-04 16:24:16

我认为您关于该组件是在 Embarcadero 产品中编写的诊断很可能是正确的。 Delphi 的运行时库确实启用了浮点异常,C++ Builder 也是如此。

Embarcaderos 工具的优点之一是浮点错误可以转换为语言异常,这使得数字编码变得更加容易。这对你来说不会有什么安慰!

整个区域是一个巨大的 PITA。对于 FP 控制字没有任何规则。这是一场完全免费的比赛。

我不认为捕获未处理的异常不会完成工作,因为 MS C++ 运行时可能已经捕获了这些异常,但我不是该领域的专家,我可能是错的。

我相信,唯一现实的解决方案是,每当执行到达代码时,将 FPU 设置为您想要的值,并在执行离开代码时恢复它。我对 COM 事件接收器了解不够,无法理解为什么它们会阻碍执行此操作。

我的产品包含一个用 Delphi 实现的 DLL,但我却遇到了相反的问题。大多数调用的客户端都有一个禁用异常的 FPU 控制字。我们采取的策略是在入口处记住8087CW,在执行代码之前将其设置为标准的Delphi CW,然后在出口处恢复它。我们也通过在进行回调之前恢复调用者的 8087CW 来小心处理回调。这是一个普通的 DLL 而不是 COM 对象,因此它可能更简单一些。

如果您决定尝试让 COM 供应商修改他们的代码,那么他们需要调用 <代码>Set8087CW()函数。

然而,由于游戏没有规则,我相信 COM 对象供应商拒绝更改其代码并将责任归还给您是合理的。

抱歉,如果这不是 100% 结论性的答案,但我无法将所有这些想法纳入评论!

I think your diagnosis that the component is written in an Embarcadero product is very likely to be true. Delphi's runtime library does indeed enable floating point exceptions, same for 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. That is going to be of little consolation to you!

This entire area is a colossal PITA. There are no rules whatsoever regarding the FP controls word. 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.

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. I don't know enough about COM event sinks to understand why they present an obstacle to doing this.

My product includes a DLL implemented in Delphi and I suffer from the reverse problem. Mostly the clients that call in have an FPU control word that disables exceptions. 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. We take care to deal with callbacks too by restoring the caller's 8087CW before making the callback. This is a plain DLL rather than a COM object so it's probably a bit simpler.

If you decide to attempt to get the COM supplier to modify their code then they need to call the Set8087CW() function.

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.

Sorry if this is not a 100% conclusive answer, but I couldn't get all these thoughts into a comment!

心头的小情儿 2024-12-04 16:24:16

虽然 FP 控制字是每个线程的,但在创建新线程时会调用 dllmain 函数,但我认为您无法避免进入新进程的这种情况。

我建议您分拆一个新进程来运行 COM,并使用您最喜欢的进程间通信方法(例如 Windows 消息、进程外 COM、命名管道、套接字等)与该进程聊天。通过这种方式,COM 服务器可以自由地造成各种损害(包括自身崩溃),而不会导致主机进程崩溃。

另一个想法是编写一个 DLL,其唯一目的是重置其 DllMain 中的 FPU,并在有问题的 DLL 之后立即加载它。当创建新线程(包括 COM 服务器创建的线程)时,Windows 可能会使用加载顺序来调用 DllMain。请注意,这取决于 Windows 的内部行为。此外,COM 服务器实际上可能依赖于 fp 异常,因为它启用了它们。禁用 FP 异常可能会导致 COM 服务器出现意外行为。

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.

I suggest you spin-off a new process to run the COM and chat with the process with your favorite inter-process communication method (e.g. windows message, out-of-proc COM, named pipe, socket, etc). In this way the COM server is free to do all kinds of damage (including crashing itself) without bring your host process down.

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. Windows is probably using the loading order to call DllMain when new threads are created, including threads created by the COM server. Note this depends on Windows's internal behavior. Also the COM server may actually depends on fp exceptions since it enables them. Disabling FP exceptions may causes the COM server to behave unexpectedly.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文