Seasons.NET

ちょっとした技術ブログです

仮想関数テーブルを出力するには?

gccのオプションには、-fdump-class-hierarchyというオプションがあり

各クラスの階層構造と仮想関数テーブルのレイアウトの表現をファイルに
ダンプします。ファイル名は、ソースファイル名に .class を追加して作成されます。
-options 形式が使用されるなら、options は -fdump-tree

と解説されています。

class Hoge
{
public:
  virtual void func(){}
};
int main()
{
  return 0;
}

をビルドすると、main.cpp.t01.class
というファイルが作成され、中に

Vtable for Hoge
Hoge::_ZTV4Hoge: 3u entries
0     (int (*)(...))0
4     (int (*)(...))(& _ZTI4Hoge)
8     Hoge::func

Class Hoge
   size=4 align=4
   base size=4 base align=4
Hoge (0x19af380) 0 nearly-empty
    vptr=((& Hoge::_ZTV4Hoge) + 8u)

という情報が出力されています。
どうやら、Vtableの2番目にfuncのアドレスが入っているようです。

ではこれの取り出し方を

#include <iostream>

class Hoge
{
    public:                   
        Hoge()                
        {
        }
        virtual void func(){ std::cout << "func" << std::endl; }
};

typedef void (*Func)();

int main( void )
{
    Hoge h;
    int *pAddr = (int *)&h; // h Address
    int *vptr = (int *)*pAddr; // vptr 
    printf( "Addr : %X\n" , *( vptr - 0 ) ); // 8     Hoge::func
    printf( "Addr : %X\n" , *( vptr - 1 ) ); // 4     (int (*)(...))(& _ZTI4Hoge)
    printf( "Addr : %X\n" , *( vptr - 2 ) ); // 0     (int (*)(...))0
    Func hoge = (Func)vptr[0];
    hoge(); //=> func
    return 0;
}

hのアドレスの先頭には、vptrが格納されているので、
hのアドレスの指す中身をint*でキャストして、vptrを取り出します。


vptrは、vtable + 8uの値がセットされているので、
先頭に格納されている中身を目的の関数ポインタでキャストしてコールすれば、
仮想関数を呼ぶ事が出来ます。