获取所有现有 vtable 的列表
在我的应用程序中,我有相当多的空指针(这是由于历史原因,应用程序最初是用纯 C 编写的)。在我的一个模块中,我知道 void 指针指向可以从已知基类继承的类的实例,但我不能 100% 确定这一点。因此,对 void 指针进行动态强制转换可能会出现问题。可能,void 指针甚至指向一个普通结构(因此结构中没有 vptr)。
我想调查 void 指针指向的内存的前 4 个字节,看看这是否是有效 vtable 的地址。我知道这是平台,甚至可能是特定于编译器版本的,但它可以帮助我推动应用程序向前发展,并在有限的时间内(假设 3 年)消除所有空指针。
有没有办法获取应用程序中所有 vtable 的列表,或者检查指针是否指向有效的 vtable,以及指向 vtable 的实例是否继承自已知的基类?
In my application I have quite some void-pointers (this is because of historical reasons, application was originally written in pure C). In one of my modules I know that the void-pointers points to instances of classes that could inherit from a known base class, but I cannot be 100% sure of it. Therefore, doing a dynamic_cast on the void-pointer might give problems. Possibly, the void-pointer even points to a plain-struct (so no vptr in the struct).
I would like to investigate the first 4 bytes of the memory the void-pointer is pointing to, to see if this is the address of the valid vtable. I know this is platform, maybe even compiler-version-specific, but it could help me in moving the application forward, and getting rid of all the void-pointers over a limited time period (let's say 3 years).
Is there a way to get a list of all vtables in the application, or a way to check whether a pointer points to a valid vtable, and whether that instance pointing to the vtable inherits from a known base class?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
你可以这样做,但你不能保证它会起作用。你甚至不知道 void* 是否会指向 vtable。上次我研究这个问题时(5 年多前),我相信某些编译器将 vtable 指针存储在实例*指向的地址之前。
它也可能是编译器选项特定的,具体取决于您使用的优化等等。
这是您能看到的推动应用程序前进的唯一选择吗?你考虑过其他人吗?
否:(
没有标准方法。您可以做的是在您最喜欢的调试器中打开一些类指针(或将内存转换为字节并将其记录到文件中)并进行比较,看看它是否有意义。即便如此,您也不能保证您的任何数据(或应用程序中的其他指针)看起来不会足够相似(当转换为字节时)以混淆您喜欢的任何代码。
又不行了。
这里有一些问题(您可能已经考虑过它们)。这些问题的答案可能会给您更多选择,或者可能会给我们提供其他想法来建议:
代码库有多大?引入全局更改是否可行,或者是否可以为此分散功能?
您是否统一对待所有指针(即:您的源代码中是否存在可以插入并添加自己的元数据的公共点?)
您可以在源代码中更改什么? (如果您有权访问内存分配子例程或者可以插入您自己的内存分配子例程,例如您可以插入您自己的元数据)。
如果在代码的不同部分将不同的数据类型转换为 void*,那么稍后您如何决定这些指针中的内容?您可以使用区分 void* 的代码来确定它们是否是类吗?
您的代码库是否允许重构方法? (通过插入部分代码的替代实现,然后删除初始实现并测试所有内容,以小规模迭代进行重构)
编辑(建议的解决方案):
执行以下步骤:
定义元数据(基)类
将内存分配例程替换为仅引用标准/旧例程的自定义例程(并确保您的代码仍然适用于自定义例程)。< /p>
在每次分配时,分配
请求的大小 + sizeof(Metadata*)
(并确保您的代码仍然有效)。将分配的前
sizeof(Metadata*)
字节替换为可以轻松测试的标准字节序列(我偏爱 0xDEADBEEF :D) 。然后,将[分配的地址] + sizeof(Metadata*)
返回给应用程序。在释放时,获取收到的指针,将其递减 `sizeof(Metadata*),然后调用系统/上一个例程来执行释放。现在,您在代码中分配了一个额外的缓冲区,专门用于每次分配的元数据。如果您对元数据感兴趣,请创建/获取元数据类指针,然后将其设置在 0xDEADBEEF 区域中。当您需要检查元数据时,
reinterpret_cast([your void* here])
,将其递减,然后检查指针值是否为 0xDEADBEEF(无元数据)或其他值。 p>请注意,此代码应该仅用于重构 - 对于生产代码来说,它很慢,容易出错,并且通常还有其他您不希望生产代码出现的坏情况。我会让所有这些代码依赖于一些
REFACTORING_SUPPORT_ENABLED
宏,这些宏永远不会让您的元数据类看到生产版本的光芒(可能除了测试版本)。You can do that, but you have no guarantees whatsoever it will work. Y don't even know if the void* will point to the vtable. Last time I looked into this (5+ years ago) I believe some compiler stored the vtable pointer before the address pointed to by the instance*.
It may also be compiler-options speciffic, depending on what optimizations you use and so on.
Is this the only option you can see for moving the application forward? Have you considered others?
No :(
No standard way. What you can do is open some class pointers in your favorite debugger (or cast the memory to bytes and log it to a file) and compare it and see if it makes sense. Even so, you have no guarantees that any of your data (or other pointers in the application) will not look similar enough (when cast as bytes) to confuse whatever code you like.
No again.
Here are some questions (you may have considered them already). Answers to these may give you more options, or may give us other ideas to propose:
how large is the code base? Is it feasible to introduce global changes, or is functionality to spread-around for that?
do you treat all pointers uniformly (that is: are there common points in your source code where you could plug in and add your own metadata?)
what can you change in your sourcecode? (If you have access to your memory allocation subroutines or could plug in your own for example you may be able to plug in your own metadata).
If different data types are cast to void* in various parts of your code, how do you decide later what is in those pointers? Can you use the code that discriminates the void* to decide if they are classes or not?
Does your code-base allow for refactoring methodologies? (refactoring in small iterations, by plugging in alternate implementations for parts of your code, then removing the initial implementation and testing everything)
Edit (proposed solution):
Do the following steps:
define a metadata (base) class
replace your memory allocation routines with custom ones which just refer to the standard / old routines (and make sure your code still works with the custom routines).
on each allocation, allocate
the requested size + sizeof(Metadata*)
(and make sure your code still works).replace the first
sizeof(Metadata*)
bytes of your allocation with a standard byte sequence that you can easily test for (I'm partial to 0xDEADBEEF :D). Then, return[allocated address] + sizeof(Metadata*)
to the application. On deallocation, take the recieved pointer, decrement it by `sizeof(Metadata*), then call the system / previous routine to perform the deallocation. Now, you have an extra buffer allocated in your code, specifically for metadata on each allocation.In the cases you're interested in having metadata for, create/obtain a metadata class pointer, then set it in the 0xDEADBEEF zone. When you need to check metadata,
reinterpret_cast<Metadata*>([your void* here])
, decrement it, then check if the pointer value is 0xDEADBEEF (no metadata) or something else.Note that this code should only be there for refactoring - for production code it is slow, error prone and generally other bad things that you do not want your production code to be. I would make all this code dependent on some
REFACTORING_SUPPORT_ENABLED
macro that would never allow your Metadata class to see the light of a production release (except for testing builds maybe).我想说,如果没有相关参考(标头声明),这是不可能的。
I would say it is not possible without related reference (header declaration).
如果您想将这些 void 指针替换为正确的接口类型,我认为可以将其自动化:
浏览代码库以获取具有虚函数的所有类的列表,您可以通过编写脚本来快速完成此操作,像 Perl
编写一个以 void* 指针作为输入的函数,然后进行迭代在这些类上尝试对其进行动态强制转换,如果成功则记录信息,例如接口类型、代码行
在任何使用 void* 指针的地方调用此函数,也许你可以用宏包装它,以便轻松获取文件、行信息
在
运行完全自动化(如果有)并分析输出。
If you want to replace those void pointers to correct interface type, here is what I think to automate it:
Go through your codebase to get a list of all classes that has virtual functions, you could do this fast by writing script, like Perl
Write an function which take a void* pointer as input, and iterate over those classes try to dynamic_cast it, and log information if succeeded, such as interface type, code line
Call this function anywhere you used void* pointer, maybe you could wrap it with a macro so you could get file, line information easy
Run a full automation (if you have) and analyse the output.
更简单的方法是为您的特定基类重载
operator new
。这样,如果您知道 void* 指针指向堆对象,那么您还可以 100% 确定它们是否指向您的对象。The easier way would be to overload
operator new
for your particular base class. That way, if you know your void* pointers are to heap objects, then you can also with 100% certainty determine whether they're pointing to your object.