较新版本的gdb允许在C ++中方便地解析vtable。
说我有这个示例代码
class Matcher {
public:
virtual void match() { cout << "base";}
};
class NMatcher: public Matcher{
public:
void match() { cout << "derived";}
};
int main() {
Matcher* m = new Matcher();
m->match();
Matcher *m2 = new NMatcher();
m2->match();
}
我可以通过info vtbl ...
(gdb) info vtbl m
vtable for 'Matcher' @ 0x400ef0 (subobject @ 0x603010):
[0]: 0x400d9a <Matcher::match()>
现在想象一下我在范围内没有变量的情况,我想检查任何虚拟对象(或任何纯虚拟基类)的vtable。为此我不严格需要一个对象。 vtable是静态的,应该可以访问。
让我们看看符号来找到vtable:
(gdb) info variables .*Matcher
All variables matching regular expression ".*Matcher":
Non-debugging symbols:
0x0000000000400ec0 vtable for NMatcher
0x0000000000400ee0 vtable for Matcher
0x0000000000400ef8 typeinfo name for NMatcher
0x0000000000400f10 typeinfo for NMatcher
0x0000000000400f28 typeinfo name for Matcher
0x0000000000400f40 typeinfo for Matcher
我无法直接使用上面列出的内存位置。虚拟方法不会从vtable的开头开始。前x个字节有偏移和RTI存在。在匹配器的情况下,它是16,但它可以是任意数字:
(gdb) p *m
$22 = {
_vptr.Matcher = 0x400ef0 <vtable for Matcher+16>
}
理论上我可以直接访问vtable的内存位置并手动检查字节:
(gdb) x /4a 0x0000000000400ee0
0x400ee0 <_ZTV7Matcher>: 0x0 0x400f40 <_ZTI7Matcher>
0x400ef0 <_ZTV7Matcher+16>: 0x400d9a <Matcher::match()>
但这太痛苦了,我想知道一种方便的做法,就像(gdb) info vtbl 'vtable for Matcher'
我正在使用GDB 7.8,但任何版本都可以。
答案 0 :(得分:1)
GDB没有提供内置的方法来做到这一点,我担心。
你可以用黑客来近似它。黑客会像这样工作:
获取vtable的基地址,例如print &'vtable for Type'
。
获取该类型的typeinfo的基地址,例如print &'typeinfo for Type'
。
逐字搜索vtable,寻找指向typeinfo的指针。下一个词是vtable指针指向的位置。
现在,用虚拟对象作为vtable指针创建一个虚拟对象。
但是,如果你有一个子对象vtable,这不会起作用。在这种情况下,你必须制作一个更正确的虚假对象。
总的来说,最好将此支持添加到gdb。它可以尝试在没有黑客的情况下做正确的事情。
以下是上述程序的工作原理:
(gdb) p &'vtable for NMatcher'
$1 = (<data variable, no debug info> *) 0x400ab0 <vtable for NMatcher>
(gdb) p &'typeinfo for NMatcher'
$2 = (<data variable, no debug info> *) 0x400ae0 <typeinfo for NMatcher>
现在转储vtable并查看指针应该在哪里。这是第二个词,因为没有虚拟基础:
(gdb) x/10a $1
0x400ab0 <_ZTV8NMatcher>: 0x0 0x400ae0 <_ZTI8NMatcher>
0x400ac0 <_ZTV8NMatcher+16>: 0x4009b2 <NMatcher::match()> 0x0
0x400ad0 <_ZTV7Matcher+8>: 0x400b08 <_ZTI7Matcher> 0x400994 <Matcher::match()>
0x400ae0 <_ZTI8NMatcher>: 0x6011e0 <_ZTVN10__cxxabiv120__si_class_type_infoE@@CXXABI_1.3+16> 0x400af8 <_ZTS8NMatcher>
0x400af0 <_ZTI8NMatcher+16>: 0x400b08 <_ZTI7Matcher> 0x65686374614d4e38
所以,做一个虚假的对象。不幸的是,你需要低劣的跑步来做到这一点: - (
(gdb) set $v = malloc(sizeof(void*))
设置假vtable指针。注意我们在指向typeinfo对象的指针之后使用一个单词:
(gdb) set *$v = ((void **) $1) + 2
现在可行:
(gdb) info vtbl (NMatcher*) $v
vtable for 'NMatcher' @ 0x400ac0 (subobject @ 0x613c20):
[0]: 0x4009b2 <NMatcher::match()>