我想调查 void-pointer 指向的内存的前 4 个字节,看看这是否是有效 vtable 的地址。
你可以这样做,但你不能保证它会起作用。Y 甚至不知道 void* 是否会指向 vtable。上次我研究这个(5 年前)我相信一些编译器将 vtable 指针存储在实例指向的地址之前*。
我知道这是平台,甚至可能是特定于编译器版本的,
它也可能是特定于编译器选项的,具体取决于您使用的优化等等。
但它可以帮助我推动应用程序向前发展,并在有限的时间段内(比如说 3 年)摆脱所有无效指针。
这是推动应用程序向前发展的唯一选择吗?你考虑过别人吗?
有没有办法获取应用程序中所有 vtables 的列表,
不 :(
或检查指针是否指向有效 vtable 的方法,
没有标准的方式。您可以做的是在您最喜欢的调试器中打开一些类指针(或将内存转换为字节并将其记录到文件中)并比较它,看看它是否有意义。即便如此,您也不能保证您的任何数据(或应用程序中的其他指针)看起来都不够相似(当转换为字节时)会混淆您喜欢的任何代码。
以及指向 vtable 的实例是否继承自已知的基类?
又不行了。
这里有一些问题(你可能已经考虑过了)。对这些问题的回答可能会给您更多选择,或者可能会给我们提供其他建议:
代码库有多大?引入全局变化是否可行,或者为此而传播的功能是否可行?
您是否统一对待所有指针(即:您的源代码中是否有可以插入并添加自己的元数据的共同点?)
你可以在你的源代码中改变什么?(如果您可以访问您的内存分配子程序或者可以插入您自己的子程序,例如您可以插入您自己的元数据)。
如果在代码的各个部分将不同的数据类型强制转换为 void*,那么以后如何确定这些指针中的内容?您可以使用区分 void* 的代码来确定它们是否是类吗?
您的代码库是否允许重构方法?(在小迭代中重构,通过插入部分代码的替代实现,然后删除初始实现并测试所有内容)
编辑(建议的解决方案):
执行以下步骤:
定义元数据(基)类
用仅引用标准/旧例程的自定义例程替换您的内存分配例程(并确保您的代码仍然适用于自定义例程)。
在每次分配时,分配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
宏,这些宏永远不会让您的元数据类看到生产版本的光芒(可能除了测试版本)。