在GDB中打印任何类的vtbl函数

时间:2016-05-13 15:10:12

标签: c++ debugging gdb

较新版本的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 ...

访问main中存在的两个变量的虚拟表
(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,但任何版本都可以。

1 个答案:

答案 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()>