VS2005 C ++ сломанные vtables - PullRequest
       19

VS2005 C ++ сломанные vtables

3 голосов
/ 24 февраля 2009

В настоящее время я работаю над довольно большой (и старой, вздохующей) кодовой базой, недавно обновленной до VS2005 (SP1). Я и моя команда меняем / обновляем / заменяем модули в этом коде по ходу работы, но мы иногда сталкиваемся с проблемами, когда кажется, что vtables не работает. Я не эксперт по vtables, но они наверняка не работают. Ошибки проявляются с этой ошибкой:

Ошибка проверки времени выполнения # 0 - значение ESP не было должным образом сохранено при вызове функции. Обычно это является результатом вызова функции, объявленной с одним соглашением о вызовах с указателем функции, объявленным с другим соглашением о вызовах.

Конечно, для этой ошибки может быть множество других причин, но при отладке (сборка отладки) я могу убедиться, что виртуальные таблицы для объекта, над которым я хочу работать, выглядят странно:

Стек и куча, которые ссылаются на каждый vtable, выглядят нормально, а указатели на vtables идеально соответствуют файлу карты. Это указывает на то, что это не ошибка перезаписи памяти или что-то подобное, так как тогда это повлияет на стек и кучу, а не на то, где хранятся виртуальные таблицы. (Они хранятся в области только для чтения, верно?) В любом случае, пока все выглядит хорошо. Но, глядя на память vtable, я обнаружил, что все значения, если я интерпретирую их как указатели, хотя они находятся в одном диапазоне (например, 0x00f203db 0x00f0f9be 0x00ecdda7 0x00f171e1), не соответствуют ни одной записи в файле карты, и многие из них даже не выровнены до 4 байтов. Я не знаю всех деталей того, как VS2005 собирает vtables, но мне это кажется неправильным. Если это правильное поведение, возможно, кто-то может мне это объяснить?

Полагаю, мой вопрос сводится к тому, что может вызвать такое поведение? Есть ли какие-либо известные ошибки в компоновщике, например, при наличии слишком сложной иерархии классов? Кто-нибудь видел что-нибудь подобное раньше? В настоящее время мы можем обойти наши сбои, переместив функции из затронутого класса во встроенный (страшная штука!), Но очевидно, что это неосуществимое долгосрочное решение.

Спасибо за понимание!

Обновление: меня попросили более подробную информацию о проекте, и, конечно, я предоставлю это. Однако, во-первых, вопрос не полностью связан с ошибкой сохранения значения ESP. Больше всего меня интересует, почему я вижу странные значения в vtable. Тем не менее, вот некоторая дополнительная информация: Решение опирается на несколько внешних и внутренних проектов, но они не менялись в течение длительного времени, все используют одно и то же соглашение о вызовах. Код, в котором кажется, что он ломается, находится внутри одного довольно стандартного C ++ «основного» проекта решения. Весь код построен с использованием одного и того же компилятора. В решении также не используются библиотеки DLL, но есть ссылки на множество статических библиотек:

SHFolder.lib, python25.lib, dxguid.lib, d3d9.lib, d3dx9.lib, dinput8.lib, ddraw.lib, dxerr9.lib, ws2_32.lib, mss32.lib, Winmm.lib, vtuneapi.lib , vttriggers.lib, DbgHelp.lib, kernel32.lib, user32.lib, gdi32.lib, winspool.lib, comdlg32.lib, advapi32.lib, shell32.lib, ole32.lib, oleaut32.lib, uuid.lib, odbc32 .lib, odbccp32.lib

Ответы [ 6 ]

2 голосов
/ 26 февраля 2009

Я нашел проблему. Глупо, но иерархия классов, которая вызвала проблему, имела виртуальную функцию GetObject, которая конфликтовала с windows #define с тем же именем. Заголовочные файлы включали эти заголовочные файлы Windows в другом порядке, что приводило в замешательство компоновщик. Итак, на самом деле проблема была в поврежденных vtables, но я не ожидал, что это будет причиной! Ну, вы чему-то учитесь каждый день ...

Однако, большое спасибо всем, кто ответил!

1 голос
/ 25 февраля 2009

Остерегайтесь эффектов, которые инкрементное связывание и Edit + Continue будут иметь на адресах функций, включая записи в V-таблице. Он работает путем косвенного вызова методов через таблицу переходов. Это позволяет компоновщику пропатчить таблицу переходов, когда ему нужно переместить метод без повторной привязки всего изображения. Адреса в этой таблице переходов разделены на 5 байтов. Они не появятся в файле .map. Это действительно легко увидеть, когда вы переключаетесь в представление Assembly и отслеживаете выполнение вызова.

Какую методику вы должны использовать для диагностики отказа RTC. Узнайте, какой метод на самом деле вызывается. Наиболее вероятная причина этого заключается в том, что вы добавили виртуальные методы в класс, но клиент этого класса не был перекомпилирован. Используя неправильный слот в V-таблице. Классически также проблема COM при смене интерфейсов, но не IID.

1 голос
/ 24 февраля 2009

Я думаю, что большая подсказка здесь в "". Обычно это результат вызова функции, объявленной с одним соглашением о вызовах, с указателем на функцию, объявленным с другим соглашением о вызовах", частью этой ошибки. Мне кажется, что существует несоответствие между API вызывающей стороны и библиотекой, которая обрабатывает вызов.

Кроме того, возможно, вы смешиваете код, созданный разными компиляторами. Что еще вы можете рассказать нам о характере этого проекта? Находится ли вызываемая вами функция во внешней библиотеке? Или вы можете отладить весь стек вызовов?

Редактировать : Вы сказали, что в проекте не используются библиотеки DLL. А как насчет статических библиотек?

0 голосов
/ 24 августа 2009

У меня была точно такая же проблема - вызов перегруженной виртуальной функции для объекта привел к ошибке «ESP не был должным образом сохранен», но отладка показала, что компилятор сгенерировал неверное смещение в vtable для этого вызова, поэтому другая вызывается функция с большим количеством параметров. Вызываемая функция обновила ESP, как если бы вызывающая сторона поместила больше параметров в стек, что, в свою очередь, привело к неверному значению ESP при возврате.

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

Надеюсь, это поможет другим, кто сталкивается с той же проблемой.

0 голосов
/ 24 февраля 2009

Когда у меня была эта ошибка, прежде чем это было всегда, когда COM был вовлечен. Почти всегда это было конкретно связано с повторным входом - вы используете COM? Используете ли вы STA, фильтры сообщений?

0 голосов
/ 24 февраля 2009

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

Я иногда обнаруживал, что команда Build может пропустить файлы, которые нужно перекомпилировать, что может привести к вашему сообщению. Опять же, полная перестройка все исправит.

...