Сторонний код изменяет управляющее слово FPU - PullRequest
10 голосов
/ 04 августа 2011

Введение - длинная и скучная часть

(вопрос в конце)

У меня сильная головная боль из-за стороннего компонента COM, который постоянно меняет управляющее слово FPU.

Моя среда разработки - Windows и Visual C ++ 2008. Обычное управляющее слово FPU указывает, что исключение не должно создаваться при различных условиях. Я проверил это как при просмотре макроса _CW_DEFAULT, найденного в float.h, так и при просмотре контрольного слова в отладчике при запуске.

Каждый раз, когда я обращаюсь к COM-объекту, управляющее слово изменяется при возврате. Это легко защитить от. Я просто сбрасываю контрольное слово, и все хорошо. Проблема в том, что компонент COM начинает вызывать мой приемник событий. Я могу защитить свой код, сбрасывая управляющее слово, как только получаю вызов события, но я ничего не могу сделать, как только я возвращаюсь из вызова события.

У меня нет источника для этого COM-компонента, но я общаюсь с автором. Ответы, которые я получил от него, были «А?». Я не думаю, что он имеет хоть малейшее представление о том, о чем я говорю, поэтому я боюсь, что должен сам что-то с этим сделать. Я считаю, что его среда выполнения (я думаю, это либо Delphi, либо Borland C ++, потому что DLL полна имен символов, все начинаются с заглавной буквы T), либо какой-то другой сторонний код, который он использует, вызывает проблему. Я не думаю, что его код явно изменяет управляющее слово FPU.

Итак, что я могу сделать? С точки зрения бизнеса крайне важно использовать этот сторонний компонент. С технической точки зрения я мог бы отказаться от него и сам реализовать протокол связи. Однако это будет очень дорого, так как этот протокол включает обработку транзакций по кредитным картам. Мы не хотим брать на себя ответственность.

Мне крайне необходим хакерский обход или некоторая полезная информация о настройках FPU в продуктах Borland, которую я могу передать автору компонента.

Вопросы

Есть ли что-нибудь, что я могу сделать? Я не думаю, что автор компонента имеет то, что нужно, чтобы исправить это (судя по его довольно невежественным ответам).

Я играю с идеей установить свой собственный обработчик исключений, в котором я просто сбрасываю управляющее слово в обработчике и приказываю Windows продолжить выполнение. Я попытался установить обработчик с SetUnhandledExceptionFilter(), но по какой-то причине исключения не перехватываются.

  1. Почему я не ловлю исключения?
  2. Если мне удастся перехватить исключения FPU, сбросить управляющее слово FPU и просто позволить продолжить выполнение, так как ничего не произошло - все ли тогда ставки?

Обновление

Я хотел бы поблагодарить всех за их предложения. Я отправил автору инструкции о том, что он может сделать, чтобы облегчить жизнь не только мне, но и многим другим клиентам его кода. Я предложил ему, чтобы он взял контрольное слово FPU на DllMain(DLL_PROCESS_ATTACH) и сохранил контрольное слово на потом, чтобы он мог сбросить FPU CW перед вызовом моих обработчиков событий и возвращением из моих вызовов.

Пока что у меня есть хакерство, если кому-то интересно. Взлом является потенциально плохим, потому что я не знаю, что он сделает с его кодом. Ранее я получил подтверждение, что он не использует числа с плавающей запятой в своем коде, так что это должно быть безопасно, за исключением некоторого стороннего кода, который он использует, который полагается на исключения FPU.

Две модификации, которые я внес в свое приложение:

  1. Обернуть мой насос сообщений
  2. Установите крюк окна (WH_CALLWNDPROC), чтобы поймать угловые случаи, когда насос сообщений обойден

В обоих случаях я проверяю, изменился ли FPU CW. Если это так, я сбрасываю его на _CW_DEFAULT.

Ответы [ 2 ]

6 голосов
/ 04 августа 2011

Хотя управляющее слово FP является для каждого потока, функции dllmain вызываются при создании новых потоков, я не думаю, что вы можете избежать этого, если не переходите к новому процессу.

Я предлагаю вам выделить новый процесс для запуска COM и пообщаться с ним с помощью вашего любимого метода межпроцессного взаимодействия (например, сообщения Windows, COM вне процесса, именованный канал, сокет и т. Д.). Таким образом, COM-сервер свободен наносить все виды повреждений (включая сам сбой), не останавливая ваш хост-процесс.

Другая идея состоит в том, чтобы написать DLL, единственная цель которой - сбросить FPU в его DllMain и загрузить его сразу после неисправной DLL. Windows, вероятно, использует порядок загрузки для вызова DllMain при создании новых потоков, включая потоки, созданные COM-сервером. Обратите внимание, что это зависит от внутреннего поведения Windows. Кроме того, COM-сервер может фактически зависеть от исключений fp, поскольку он их разрешает. Отключение исключений FP может привести к непредвиденной работе COM-сервера.

6 голосов
/ 04 августа 2011

Я думаю, что ваш диагноз того, что компонент записан в продукте Embarcadero, очень вероятен.Библиотека времени выполнения Delphi действительно разрешает исключения с плавающей точкой, то же самое для C ++ Builder.

Одна из приятных особенностей инструментов Embarcaderos заключается в том, что ошибки с плавающей точкой преобразуются в исключения языка, что значительно упрощает числовое кодирование.Это будет небольшим утешением для вас!

Вся эта область - колоссальная ПИТА.Нет никаких правил относительно слова управления FP.Это абсолютно бесплатно для всех.

Я не верю, что перехват необработанных исключений не выполнит работу, потому что среда выполнения MS C ++, вероятно, уже будет перехватывать эти исключения, но янет эксперта в этой области, и я могу ошибаться.

Я считаю, что ваше единственное реалистичное решение - установить FPU таким, каким вы хотите его видеть, когда выполнение приходит в ваш код, и восстанавливать его, когда выполнение покидаеткод.Я не знаю достаточно о приемниках событий COM, чтобы понять, почему они препятствуют этому.

Мой продукт включает в себя DLL, реализованную в Delphi, и я страдаю от обратной проблемы.В основном клиенты, которые вызывают, имеют управляющее слово FPU, которое отключает исключения.Стратегия, которую мы принимаем, заключается в том, чтобы помнить 8087CW при входе, установить его в стандартную Delphi CW перед выполнением кода, а затем восстановить его в точке выхода.Мы позаботимся и о обратных вызовах, восстановив 8087CW вызывающего абонента перед выполнением обратного вызова.Это простая DLL, а не COM-объект, так что, возможно, это немного проще.

Если вы решите попытаться заставить поставщика COM изменить свой код, тогда ему нужно вызвать Set8087CW() function.

Однако, поскольку в игре нет правил, я считаю, что поставщик COM-объектов был бы оправдан, если бы он отказался изменить свой код и возложить на вас ответственность.

Извините, если это не на 100% окончательный ответ, но я не смог все эти мысли включить в комментарий!

...