Неправильная компоновка vtable для класса, экспортируемого DLL: запрос на уточнение относительно заголовков и конструкции vtable - PullRequest
0 голосов
/ 11 августа 2010

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

Фон

  1. Два отдельных VC6.0проекты: один для exe, один для dll.
  2. Приложение содержит файлы .lib, .dll и .h из выпусков проекта dll.
  3. Когда новый выпуск готов, .libФайлы .dll и .h копируются в проект exe из проекта dll.
  4. Я унаследовал эту схему.

Проблема:

Недавно я внес изменения в DLL и скопировал файлы .lib и .dll, но забыл скопировать файлы заголовков .Произошли некоторые изменения в иерархии, и, как следствие, изменилась vtable для одного конкретного класса (назовите его InternalClass).

exe не напрямую не вызывает никаких методов на InternalClass.Вместо этого он создает экземпляр другого класса (назовите его InterfaceClass), который инкапсулирует указатель на объект InternalClass и вызывает различные методы для этого указателя.

Во время выполнения вызовы выполняются из InterfaceClass методы к InternalClass методы фактически вызывали неправильные методы (то есть InterfaceClass будет вызывать InternalClass::A и InternalClass::B будет фактически выполняться).Глядя на asm , выясняется, что исправление или thunk (извините, если это неправильный жаргон!) Использовал правильное смещение в vtable. Однако , сама таблица содержит список указателей, которые можно ожидать от старых заголовочных файлов.

Актуальный вопрос:

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

Когда определяется макет для vtables и какая информация используется для создания vtable для этих классов во время выполнения?То есть мне кажется, что правильная vtable должна была быть собрана при компиляции dll, чтобы смещения и т. Д. Могли использоваться для вызовов от InterfaceClass до InternalClass.Почему тогда во время выполнения использовался устаревший vtable?Разметку также определяют отдельно, когда exe-файл компилируется с использованием заголовков?

Я не уверен, что это вообще понятно. Дайте мне знатьесли то, что я спрашиваю, слишком запутанно.Спасибо!

Ответы [ 2 ]

2 голосов
/ 11 августа 2010

Хм, подробности реализации для 12-летнего компилятора. Когда вы используете __declspec (dllexport) для класса, компоновщик экспортирует членов класса. Не в таблице. Это восстанавливается в клиенте класса, когда компилятор анализирует объявление класса в заголовочном файле. Компоновщик заполняет этот локальный vtable адресами экспортированных членов.

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

1 голос
/ 11 августа 2010

То, как вы описываете взаимодействие этих двух классов, не совсем ясно по одной очень важной детали: ваш так называемый InternalClass экспортируется из dll или нет?Или поставить вопрос по-другому: использует ли код в этих скопированных заголовочных файлах указатели на InternalClass или все знания этого класса, скрытые исключительно в dll?

Если эторассматриваемый класс фактически полностью скрыт внутри dll, тогда я не уверен, как эта проблема может произойти.Но из того, что вы описываете, это действительно звучит так, как будто InternalClass предоставляется библиотекой DLL.

Если те заголовки, которые копируются, содержат код, схожий по своей концепции с:* Тогда InternalClass должен быть выставлен, иначе проект exe не сможет скомпилировать заголовок.(В конце концов, это не совсем внутреннее.)

И если это так, то это легко объясняет вашу проблему.Exe строится против другой декларации InternalClass, чем была создана dll.Это означает, что каждый компилирует свою собственную версию виртуальной таблицы, и эти версии различны.

Затем во время выполнения исполняемый код передается объекту, созданному из dll, и, таким образом, указатель vtable указывает наверсия dll vtable.Затем исполняемый код пытается работать через этот указатель виртуальной таблицы, как если бы он указывал на исполняемую версию виртуальной таблицы.И, очевидно, это вызывает проблемы.

...