什么是非虚拟 thunk?

逆向工程 C++ 虚函数 海湾合作委员会
2021-07-03 06:54:56

在实际代码中,我遇到了一些“非虚拟 thunk”函数:

; `non-virtual thunk to'QTextCodecPlugin::create(QString  const&)
    EXPORT _ZThn8_N16QTextCodecPlugin6createERK7QString
_ZThn8_N16QTextCodecPlugin6createERK7QString
   SUB.W   R0, R0, #8
   B.W     _ZN16QTextCodecPlugin6createERK7QString; QTextCodecPlugin::create(QString  const&)

; End of function `non-virtual thunk to'QTextCodecPlugin::create(QString  const&)


; `non-virtual thunk to'QTextCodecPlugin::~QTextCodecPlugin()
    EXPORT _ZThn8_N16QTextCodecPluginD1Ev

_ZThn8_N16QTextCodecPluginD1Ev
    SUB.W  R0, R0, #8
    B.W    _ZN16QTextCodecPluginD2Ev; QTextCodecPlugin::~QTextCodecPlugin()

; End of function `non-virtual thunk to'QTextCodecPlugin::~QTextCodecPlugin()

(GCC、C++、Android NDK、Qt、Necessitas、ARM)

c++filt 说:

$ c++filt _ZThn8_N16QTextCodecPlugin6createERK7QString
non-virtual thunk to QTextCodecPlugin::create(QString const&)
$ c++filt _ZThn8_N16QTextCodecPluginD1Ev
non-virtual thunk to QTextCodecPlugin::~QTextCodecPlugin()

这些“非虚拟 thunk” 做什么以及为什么做什么?

2个回答

这些是用于实现多重继承的对象虚拟表条目。

简而言之,sub指令是将正确的派生类对象大小偏移到虚拟表;但是下面的(长)文章比我在这里能更好地解释:http : //thomas-sanchez.net/computer-sciences/2011/08/15/what-every-c-programmer-should-知道最难的部分/

更新:上面的链接不再有效,但在 webarchive.org 上有此页面的副本:http ://web.archive.org/web/20131210001207/http://thomas-sanchez.net/computer-sciences/ 2011/08/15/每个c程序员应该知道的难点/

答案应该真正说明虚拟 thunk 和非虚拟 thunk 之间的区别。它们的操作相同,只是名称不同。虚拟继承基类的 thunk 称为虚拟 thunk,基对象将在对象的末尾,而对象不在对象开头的常规继承类的 thunk 称为非虚拟 thunk .

如果 C 继承自 A 和 B,则 A 的虚拟指针将位于对象的开头(将 C 的 A 的 vtable 指针粘贴在它上面),因此在调用类型方法时不需要指向开头的指针偏移带有 A* 的 C* 和子对象 B(将 C 的 B 的 vtable 指针粘贴在它上面)将位于对象中的偏移处。B 的虚拟表必须包含一个非虚拟 thunk,它将偏移到指向对象开头的指针,因为被虚拟调用的方法是接受指向 C 对象的指针的 C 方法之一,如果这个虚拟表被使用,那么它将接收一个指向 B 的多态转换指针,因此表中的调用需要是一个 thunk。

如果 D 继承自 B 和 C,而 B 和 C 虚拟地继承自 A,那么这意味着 B 的虚拟表指针将位于对象的开头(D 的 B 的 vtable 粘贴在它上面)和 C 的 vtable 指针(将 C 的 D 的 vtable 指针粘贴在它上面)将处于一个偏移处,并且在调用它时需要一个非虚拟 thunk,这将是当它被调用的对象指针被强制转换为 C* 类型时。A 和它的 vtable 指针(将 A 的 D 的 vtable 粘贴在它上面)将位于对象的末尾,并且需要在 vtable 中使用虚拟 thunk 而不是实际的虚拟方法,并且虚拟 thunk 调用实际的虚拟方法,当该方法在转换为 A* 的对象指针上调用时,它会被调用。

虚拟 thunk 将对象地址偏移例如 - 32(无论虚拟基础对象在哪里),非虚拟 thunk 将对象地址偏移例如 - 16(无论第二个子对象在哪里)。基本上,当调用在最派生类中定义的方法时,虚拟 thunk 会将虚拟继承类类型的对象偏移到对象的开头。虚拟 thunk 用于虚拟继承的类,它们在对象中只有 1 个特殊对象,而不是多个。如果您没有虚拟继承,则对象中将有多个这些对象,并且在调用最派生类中的方法时,您可以使用非虚拟 thunk 来抵消它们