在我的应用程序中,我有一些无效指针(这是由于历史原因,应用程序最初是用纯C编写的)。在我的一个模块中,我知道void-pointers指向可以从已知基类继承的类的实例,但我无法100%确定它。因此,对void-pointer执行dynamic_cast可能会产生问题。可能,void-pointer甚至指向一个plain-struct(结构中没有vptr)。
我想研究void-pointer指向的内存的前4个字节,看看这是否是有效vtable的地址。我知道这是平台,甚至可能是特定于编译器版本的,但它可以帮助我推进应用程序,并在有限的时间内摆脱所有的空指针(让我们说3年)。
有没有办法获取应用程序中所有vtable的列表,或者检查指针是否指向有效vtable的方法,以及指向vtable的实例是否继承了已知的基类?
答案 0 :(得分:4)
我想调查第一个 void-pointer的4个字节的内存 指着,看看这是不是 有效的vtable的地址。
你可以这样做,但你无法保证它能起作用。 Y甚至不知道空白*是否会指向vtable。上次我调查这个(5年多前)我相信一些编译器在实例*指向的地址之前存储了vtable指针。
我知道这是平台,甚至可能 编译器特定于版本的,
它也可能是编译器选项特有的,具体取决于您使用的优化等等。
但它可以帮助我移动 申请转发,并摆脱 所有虚空指针的一个 有限的时间段(让我们说3 年)。
这是您可以看到的推动应用程序前进的唯一选择吗?你考虑过其他人吗?
有没有办法获得所有的清单 应用程序中的vtable,
否:(
还是一种检查是否有指针的方法 指向有效的vtable,
没有标准的方法。您可以做的是在您喜欢的调试器中打开一些类指针(或将内存转换为字节并将其记录到文件中)并比较它并查看它是否有意义。即便如此,您也无法保证您的任何数据(或应用程序中的其他指针)看起来不够相似(当转换为字节时)以混淆您喜欢的任何代码。
以及该实例是否指向 vtable继承自已知的基础 类?
不再。
以下是一些问题(您可能已经考虑过了)。对这些问题的答案可能会给你更多选择,或者可能会给我们提出其他建议:
代码库有多大?是否可以引入全局变更,或者是为此而传播的功能?
您是否统一处理所有指针(即:您的源代码中是否有可以插入并添加自己的元数据的公共点?)
您可以在源代码中更改哪些内容? (如果您可以访问内存分配子例程,或者可以插入自己的内存,例如,您可以插入自己的元数据)。
如果在代码的各个部分中将不同的数据类型转换为void *,那么稍后如何决定这些指针中的内容?您是否可以使用区分void *的代码来决定它们是否是类?
您的代码库是否允许重构方法? (通过插入部分代码的替代实现,然后删除初始实现并测试所有内容,在小型迭代中进行重构)
修改(建议的解决方案):
执行以下步骤:
定义元数据(基础)类
将您的内存分配例程替换为仅引用标准/旧例程的自定义内存分配例程(并确保您的代码仍然可以使用自定义例程)。
在每次分配时,分配the requested size + sizeof(Metadata*)
(并确保您的代码仍然有效)。
使用您可以轻松测试的标准字节序列替换分配的第一个 sizeof(Metadata*)
字节(我偏向于0xDEADBEEF:D)。然后,将[allocated address] + sizeof(Metadata*)
返回给应用程序。在重新分配时,取出接收的指针,将其减去`sizeof(元数据*),然后调用系统/上一个例程来执行释放。现在,您的代码中分配了额外的缓冲区,特别是每个分配的元数据。
如果您对使用元数据感兴趣,请创建/获取元数据类指针,然后将其设置在0xDEADBEEF区域中。当您需要检查元数据reinterpret_cast<Metadata*>([your void* here])
时,递减它,然后检查指针值是否为0xDEADBEEF(无元数据)或其他内容。
请注意,此代码应仅用于重构 - 对于生产代码而言,它很慢,容易出错,而且通常是您不希望生产代码出现的其他不良内容。我会使所有这些代码依赖于某些REFACTORING_SUPPORT_ENABLED
宏,它永远不会允许您的元数据类看到生产版本的亮点(测试版本可能除外)。
答案 1 :(得分:0)
我想说没有相关参考(标题声明)就不可能。
答案 2 :(得分:0)
如果你想将这些void指针替换为正确的接口类型,我认为这是自动化它:
通过您的代码库获取所有具有虚函数的类的列表,您可以通过编写脚本来快速完成此操作,例如Perl
编写一个以void *指针作为输入的函数,并迭代这些类尝试dynamic_cast它,如果成功则记录信息,如接口类型,代码行
在你使用void *指针的任何地方调用这个函数,也许你可以用宏包装它,这样你就可以轻松获取文件,行信息
运行完全自动化(如果有)并分析输出。
答案 3 :(得分:0)
更简单的方法是为您的特定基类重载operator new
。这样,如果你知道你的void *指针是堆对象,那么你也可以100%确定它们是否指向你的对象。