【问题标题】:How to watch the vtable in gdb in a C++ program?如何在 C++ 程序中查看 gdb 中的 vtable?
【发布时间】:2011-12-02 11:02:33
【问题描述】:

我的一个 C++ 程序有一个有趣的问题。显然,我的一个类的 vtable 在程序执行期间/变得混乱。在 gdb 会话中,我发现如果我直接调用对象的方法,它会成功,但如果我使用指针或引用,我最终会成为一个完全不相关的类的析构函数,该类不会很快被实例化。当然没有 this-Pointer 改变。

假设我的观察是正确的,我如何在 gdb 中查看对象的 vtable?我在 Linux 上使用 gcc,我的 gdb 版本是 GNU gdb (Ubuntu/Linaro 7.3-0ubuntu2) 7.3-2011.08

【问题讨论】:

  • 对此我不是 100%,所以我不会将其作为答案发布,但看看 ((void*)this)-1 应该就足够了(指针长度 before this) 获得一个指向实际函数的指针数组的指针(0 终止?)。它们不会携带方法名称或任何东西,但这就是 vtable 的全部内容。
  • 您是否使用 Valgrind 或任何类似工具分析您的代码?
  • 你怎么知道你最终会使用不同的功能?你在调试或输出什么东西吗?
  • 正如@Als 所说,使用valgrind。您通常无法修改 vtable,因为 is 在受保护的内存中,但很可能会弄乱 vptr,使其指向其他内容。
  • 我正在调试。我在虚函数内部设置了一个断点。如果我通过对象调用它会被击中,但如果我通过指针调用则不会。 - 我现在要试试 valgrind,谢谢你的提示。

标签: c++ gcc gdb virtual-functions


【解决方案1】:

您可以使用 gcc 的 -fdump-class-hierarchy 选项,它会为您提供 vtable 信息,但输出可能非常冗长且难以阅读。

例如,给定以下琐碎的类:

class Base {                                                                            
    public:                                                                             
        virtual int method() = 0;                                                       
};                                                                                      

class Derived : public Base {                                                           
    public:                                                                             
        int method() {                                                                  
            return 10;                                                                  
        }                                                                               
};  

相关的输出是

Vtable for Base
Base::_ZTV4Base: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4Base)
16    (int (*)(...))__cxa_pure_virtual

Class Base
   size=8 align=8
   base size=8 base align=8
Base (0x7f14c308ccc0) 0 nearly-empty
    vptr=((& Base::_ZTV4Base) + 16u)

Vtable for Derived
Derived::_ZTV7Derived: 3u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI7Derived)
16    (int (*)(...))Derived::method

Class Derived
   size=8 align=8
   base size=8 base align=8
Derived (0x7f14c2ee7208) 0 nearly-empty
    vptr=((& Derived::_ZTV7Derived) + 16u)
  Base (0x7f14c308cd20) 0 nearly-empty
      primary-for Derived (0x7f14c2ee7208)

这应该让您了解在调试等期间预期的地址范围。

【讨论】:

【解决方案2】:

除非你是黑客,否则我怀疑你的 vtable 会搞砸。你是从 costructor 调用虚函数吗?

也可能是调试器惹恼了你。通过优化编译,您可能会为执行相同操作的函数获得相同的地址,因为没有重复的代码。我在 Windows 下遇到了这个问题,其中 Visual Studio 也跳入了实际上做同样事情的不同功能。尝试输出一些东西,而不是使用调试器遍历代码...这可能是原因。

【讨论】:

  • 后人注意:vtables 可能会以多种方式搞砸。攻击 vtable 的“黑客”通常只是攻击 OP 可能试图压制的那种错误。一个好的经验法则是怀疑缓冲区溢出或释放后使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 1970-01-01
  • 2017-09-23
  • 2011-11-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多