打印GDB中任何类的vtbl函数

Print vtbl functions of any class in GDB

本文关键字:vtbl 函数 任何类 GDB 打印      更新时间:2023-10-16

gdb的较新版本允许在C++中方便地解析vtables。

假设我有这个样本代码

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' 的方便方法

我使用的是GDB7.8,但任何版本都可以。

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