【发布时间】:2011-02-24 03:12:42
【问题描述】:
假设你有一个 C++ 类,例如:
class Foo {
public:
virtual ~Foo() {}
virtual DoSomething() = 0;
};
C++ 编译器将调用转换为 vtable 查找:
Foo* foo;
// Translated by C++ to:
// foo->vtable->DoSomething(foo);
foo->DoSomething();
假设我正在编写一个 JIT 编译器,并且我想获取 Foo 类的特定实例的 DoSomething() 函数的地址,因此我可以生成直接跳转到它的代码,而不是进行表查找和间接分支。
我的问题是:
是否有任何标准的 C++ 方法可以做到这一点(我几乎可以肯定答案是否定的,但为了完整起见想问一下)。
是否有任何远程独立于编译器的方式来执行此操作,例如有人实现的库,它提供了用于访问 vtable 的 API?
我对黑客完全开放,如果他们能工作的话。例如,如果我创建了自己的派生类并且可以确定它的 DoSomething 方法的地址,我可以假设 vtable 是 Foo 的第一个(隐藏的)成员并搜索它的 vtable 直到找到我的指针值。但是,我不知道获取此地址的方法:如果我写 &DerivedFoo::DoSomething,我会得到一个指向成员的指针,这是完全不同的。
也许我可以将指向成员的指针转换为 vtable 偏移量。当我编译以下内容时:
class Foo {
public:
virtual ~Foo() {}
virtual void DoSomething() = 0;
};
void foo(Foo *f, void (Foo::*member)()) {
(f->*member)();
}
在 GCC/x86-64 上,我得到这个程序集输出:
Disassembly of section .text:
0000000000000000 <_Z3fooP3FooMS_FvvE>:
0: 40 f6 c6 01 test sil,0x1
4: 48 89 74 24 e8 mov QWORD PTR [rsp-0x18],rsi
9: 48 89 54 24 f0 mov QWORD PTR [rsp-0x10],rdx
e: 74 10 je 20 <_Z3fooP3FooMS_FvvE+0x20>
10: 48 01 d7 add rdi,rdx
13: 48 8b 07 mov rax,QWORD PTR [rdi]
16: 48 8b 74 30 ff mov rsi,QWORD PTR [rax+rsi*1-0x1]
1b: ff e6 jmp rsi
1d: 0f 1f 00 nop DWORD PTR [rax]
20: 48 01 d7 add rdi,rdx
23: ff e6 jmp rsi
我不完全理解这里发生了什么,但如果我可以对此进行逆向工程或使用 ABI 规范,我可以为每个单独的平台生成一个类似上述的片段,作为从虚表。
【问题讨论】:
-
这不是一个真正的答案,但您应该阅读codesourcery.com/cxx-abi。忽略提及安腾; GCC 普遍使用该 ABI(带有适当的特定于处理器的调整),现在几乎所有其他用于 *nix 的编译器也是如此。 MSVC 当然是不同的,但可能没有那么不同——只有这么多方法可以做到。
-
您应该研究如何使用调试符号。如果符号可用,这种事情就很容易了。我回答了这个问题stackoverflow.com/questions/5740155/access-v-table-at-run-time/…,并提供了有关如何访问 vtable 指针、它们的布局、它们的来源等的参考资料……查看我对维基百科关于 C++ RTT 信息的页面的参考,这可能会对你有所帮助en.wikipedia.org/wiki/Run-time_type_information跨度>
标签: c++ cross-platform