Получение списка всех существующих vtables - PullRequest
0 голосов
/ 14 июня 2010

В моем приложении у меня есть довольно много void-указателей (это по историческим причинам, приложение изначально было написано на чистом C). В одном из моих модулей я знаю, что указатели void указывают на экземпляры классов, которые могут наследоваться от известного базового класса, но я не могу быть на 100% уверен в этом. Следовательно, выполнение dynamic_cast для void-указателя может вызвать проблемы. Возможно, указатель void даже указывает на обычную структуру (поэтому в структуре нет vptr).

Я хотел бы исследовать первые 4 байта памяти, на которую указывает указатель void, чтобы узнать, является ли это адрес действительной таблицы. Я знаю, что это платформа, может быть, даже для конкретной версии компилятора, но это может помочь мне продвинуть приложение вперед и избавиться от всех пустотных указателей за ограниченный период времени (скажем, 3 года).

Есть ли способ получить список всех vtables в приложении или способ проверить, указывает ли указатель на действительный vtable, и наследует ли этот экземпляр, указывающий на vtable, от известного базового класса?

Ответы [ 4 ]

4 голосов
/ 14 июня 2010

Я хотел бы исследовать первый 4 байта памяти void-указатель указывает, чтобы увидеть, является ли это адрес действующего vtable.

Вы можете сделать это, но у вас нет никаких гарантий, что это сработает. Вы даже не знаете, будет ли void * указывать на vtable. В прошлый раз, когда я изучал это (5+ лет назад), я считаю, что какой-то компилятор хранил указатель vtable до адреса, на который указывает экземпляр *.

Я знаю, что это платформа, может быть, даже компилятором конкретной версии,

Это также может быть специфично для параметров компилятора, в зависимости от того, какую оптимизацию вы используете и т. Д.

но это могло бы помочь мне в приложение вперед, и избавиться из всех пустых указателей над ограниченный период времени (скажем, 3 лет).

Это единственная опция, которую вы можете видеть для продвижения приложения вперед? Вы рассматривали других?

Есть ли способ получить список всех vtables в приложении,

Нет: (

или способ проверить, есть ли указатель указывает на действительный vtable,

Нет стандартного способа. Что вы можете сделать, это открыть некоторые указатели классов в вашем любимом отладчике (или преобразовать память в байты и записать ее в файл), сравнить ее и посмотреть, имеет ли это смысл. Тем не менее, у вас нет никаких гарантий, что какие-либо ваши данные (или другие указатели в приложении) не будут выглядеть достаточно похожими (при преобразовании в байты), чтобы перепутать любой код, который вам нравится.

и указывает ли этот экземпляр на vtable наследуется от известной базы класс

Нет снова.

Вот несколько вопросов (возможно, вы их уже рассмотрели). Ответы на них могут дать вам больше возможностей или предложить другие идеи:

  • насколько велика база кода? Реально ли вводить глобальные изменения или для этого нужно распространить функциональность?

  • Обрабатываете ли вы все указатели единообразно (то есть: есть ли в вашем исходном коде общие точки, где вы можете подключить и добавить свои собственные метаданные?)

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

  • Если разные типы данных приводятся к пустоте * в различных частях вашего кода, как вы решите позже, что находится в этих указателях? Можете ли вы использовать код, который различает пустоту *, чтобы решить, являются ли они классами или нет?

  • Допускает ли ваша кодовая база методологии рефакторинга? (рефакторинг в маленьких итерациях, путем добавления альтернативных реализаций для частей вашего кода, затем удаления начальной реализации и тестирования всего)

Редактировать (предлагаемое решение):

Выполните следующие шаги:

  • определить метаданные (базовый) класс

  • замените ваши подпрограммы выделения памяти на пользовательские, которые просто ссылаются на стандартные / старые подпрограммы (и убедитесь, что ваш код все еще работает с пользовательскими подпрограммами).

  • при каждом выделении выделите the requested size + sizeof(Metadata*) (и убедитесь, что ваш код все еще работает).

  • замените первые sizeof(Metadata*) байтов вашего выделения стандартной последовательностью байтов, которую вы можете легко проверить (я неравнодушен к 0xDEADBEEF: D). Затем верните [allocated address] + sizeof(Metadata*) в приложение. При освобождении возьмите полученный указатель, уменьшите его на `sizeof (Metadata *), затем вызовите системную / предыдущую подпрограмму для выполнения освобождения. Теперь у вас есть дополнительный буфер, выделенный в вашем коде, специально для метаданных при каждом выделении.

  • В тех случаях, когда вам нужно иметь метаданные, создайте / получите указатель класса метаданных, а затем установите его в зоне 0xDEADBEEF. Когда вам нужно проверить метаданные, reinterpret_cast<Metadata*>([your void* here]), уменьшите их, затем проверьте, является ли значение указателя 0xDEADBEEF (без метаданных) или что-то еще.

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

0 голосов
/ 14 июня 2010

Более простым способом было бы перегрузить operator new для вашего конкретного базового класса.Таким образом, если вы знаете, что ваши void * указатели предназначены для кучи объектов, то вы также можете со 100% уверенностью определить, указывают ли они на ваш объект.

0 голосов
/ 14 июня 2010

Если вы хотите заменить эти пустые указатели на правильный тип интерфейса, вот что я думаю, чтобы автоматизировать это:

  1. Пройдите через свою кодовую базу, чтобы получить список всех классов, которые имеютВиртуальные функции, вы можете сделать это быстро, написав скрипт, например, Perl

  2. Напишите функцию, которая принимает указатель void * в качестве входных данных, итерируя по этим классам, попытайтесь выполнить его динамическую трансляцию и запишитеинформация в случае успеха, такая как тип интерфейса, строка кода

  3. Вызывайте эту функцию везде, где вы использовали указатель void *, возможно, вы могли бы обернуть ее макросом, чтобы вы могли легко получить файл, информацию о строке

  4. Запустите полную автоматизацию (если есть) и проанализируйте вывод.

0 голосов
/ 14 июня 2010

Я бы сказал, что это невозможно без соответствующей ссылки (объявление заголовка).

...