之前已经提出过,但问题略有不同,答案都非常无益,所以我再试一次。
我需要来自编译器的2条信息,这些信息似乎难以提取。
我想在没有类的实例的情况下找到给定类的vtable指针。我可以在二进制文件中的任何地方找到vtable符号的唯一引用是在它被分配给新实例的构造函数中,并且在没有调用它的情况下将指针移出构造函数真的很尴尬。我想知道是否有人可以指出常见编译器(msvc,gcc,clang)的vtable名称修改规范,所以我可以明确地对符号进行外设吗? (我还没有找到这个)。我对此的担心是,我怀疑(至少在VC上)符号名称中有一些字符在C ++标识符中是非法的,因此我不确定如何创建链接到它的变量...
我需要方法的实际函数指针。接近成员函数指针似乎唯一可用的语法是指向成员的运算符,这会产生非常符合编译器的输出。
我观察到,GCC / Clang产生了一个漂亮的小结构; {void * ptr_or_offset; size_t susp_vtable; }。从那里,它很容易找到实际的函数指针(假设我有vtable指针!见#1)。
MSVC有点困难;指向虚拟成员的指针是指向执行虚拟查找的thunk函数的指针。似乎thunk与vtable偏移量相关,因此每个vtable偏移量都有一个thunk。这种策略很难;识别方法是否为虚拟,如果是,则获取vtable偏移量(因此得到实际的函数指针)。我想也许我可以为每个vtable偏移量制作一个thunk指针表,直到N,然后当我拿一个指向成员的指针时,我可以将它与thunk表中的每个项目进行比较;如果它是其中之一,我知道它是虚拟的,并且vtable偏移,所以我可以得到指针。所以,这一切听起来都很可怕,但它就是这样,因为C ++并不觉得语法应该可用于获取这些基本语言原语,并且由于某些未知原因而没有合适的代表!
有人能想到更好或更直接的机制来捕获我寻求的那些数据吗?或者任何可提高便携性的替代解决方案也会很酷!
干杯!
编辑:考虑到其他类似的帖子已经充满了人们说“它不便携”,并且“不做”#39;我想请求你不要用更多相同的东西污染这个线程。他们是毫无价值的评论,没有解决这个问题。这个问题需要一些创造性思维,让我们对解决方案的质量留下深刻印象。
编辑2:不确定我为什么被投票。这是一个有趣且很难解决的问题。关于互联网的讨论非常少。
答案 0 :(得分:0)
虚拟表根本不是c ++标准的全部内容。在整个ISO标准中没有关于它的说法。它只是一种常用的实施方法。这就是为什么您找不到工具支持来做您想做的事情。
此外,虚拟表可能是一个非常复杂的问题,例如在多重继承的情况下,在同一对象中使用几个不同的虚拟表(对于不同的子对象)。
你已经找到了一种精确的方式来获取一些信息,并且显然已经详细研究了这一点。但是,我不确定你找到vtable偏移的方法是否适用于所有不同的条件。我强烈建议你考虑一下你的问题并找到另一种方法来实现它(除非你正在研究新的调试工具并且别无选择;-))
答案 1 :(得分:0)
这只是一个提示,并且不确定它是否与您实际问题相关。
如果您需要访问底层的vTable(这只是实现细节),您应该考虑显式的vTable管理。这意味着所有动态类都应该没有虚函数,而只是指向显式vtable的指针,该指针包含指向函数(而不是成员函数)的指针,其第一个参数应该是指向该类对象的指针(或ref)。 p>
然后,您应该明确地使用invoke
函数或宏,该函数或宏稍后会调用类似inner_invocation(object, offset_of_method, other_args...)
的内容。一旦你到那里就可以手动调用 实际的虚拟成员函数。
好吧,它看起来真的像C中的C ++,但至少它可以在不过多依赖编译器实现细节的情况下掌握操作。
再次阅读您的问题和评论之后,这是一个简单的方法来进行动态推导。这就是Java程序员所称的代理:代理(Java sense)是一个瘦实例,它在真实对象上实现 interface (仅包含纯虚方法的类)。好的,Java提供了创建代理的所有机制,而C ++并不是那么友好,但可扩展性足以提供像它一样构建一些思路的方法。
你应该忘记vtables并将它们视为接口 - 顺便说一句,你获得了可移植性。
以下示例说明如何在类上实现两个接口:
#include <iostream>
#include <string>
#include <sstream>
// definition of template class proxy
template<class C, class ... I>
class Proxy: public C, public I... {
private:
Proxy() {};
public:
Proxy(const C& c): C(c) {}; // uses copy constructor on original object
virtual ~Proxy() {};
};
/* if the proxy is not required to inherit from C, you can take a ref
template<class C, class ... I>
class Proxy: public C, public I... {
private:
C& obj;
Proxy() {};
public:
Proxy(const C& c): obj(c) {}; // just copy the reference
virtual ~Proxy() {};
};
*/
// macro definition to help in proxy declarations
#define BEGIN_DECLARE_PROXY(proxy, cls, ...) \
class proxy: public Proxy<cls, __VA_ARGS__> { \
public: \
proxy(const C& c): Proxy(c) {}; \
#define IMPLEMENT0(type, method, function) \
type method() { \
return function(*this ); \
}
#define IMPLEMENT1(type, method, function, typ1, arg1) \
type method(typ1 arg1) { \
return function(*this, arg1 ); \
}
#define IMPLEMENT2(type, method, function, typ1, arg1, typ2, arg2) \
type method(typ1 arg1, typ2 arg2) { \
return function(*this, arg1, arg2); \
}
#define IMPLEMENT3(type, method, function, typ1, arg1, typ2, arg2, typ3, arg3) \
type method(typ1 arg1, typ2 arg2, typ3, arg3) { \
return function(*this, arg1, arg2, arg3); \
}
// could add many others - could not use VA_ARGS here ...
/* if we used the ref. version, implementation should be return function(obj, ...); */
#define END_DECLARE_PROXY };
// example actual class
struct C {
int val;
std::string name;
};
// example interfaces
class I {
public:
virtual std::string display() = 0;
};
class I2 {
public:
virtual void show(std::ostream& out) = 0;
};
// function implementing the interfaces
std::string C_display(const C& c) {
std::stringstream ss;
ss << c.name << " (" << c.val << ")";
return ss.str();
}
void C_show(const C& c, std::ostream& out) {
out << C_display(c) << std::endl;
}
// actual proxy definition
BEGIN_DECLARE_PROXY(CI, C, I, I2)
IMPLEMENT0(std::string, display, C_display)
IMPLEMENT1(void, show, C_show, std::ostream&, out)
END_DECLARE_PROXY
int main() {
C c = {12, "Foo"}; // create an object
CI ci(c); // build a proxy around the object
I& i = ci; // an interface on the proxy
// example calls
std::cout << i.display() << std::endl;
ci.show(std::cout);
return 0;
}
答案 2 :(得分:0)
我一直在努力解决这个问题,我已经将它用于lib:https://github.com/TurkeyMan/virtualwrangler
完成了99%,我只需要一个GCC实现:
template <typename C>
inline VTable GetVTable();
有人能想出任何创造性的方法来强迫GCC为某些班级制作vtable ptr吗? 我最好的是:
extern "C" void *_ZTV7MyClass;
哪个没关系,除了符号名称中有数字'7',这是该类名称的字符串长度!!这意味着我无法将该声明用于宏...除非有一些聪明的方法来执行预处理器stringlen ...?
想法?