36

仮想メンバー関数のアドレスを出力しようとしています。関数を実装するクラスがわかっている場合は、次のように記述できます。

print("address: %p", &A::func);

しかし、私はこのようなことをしたい:

A *b = new B();

printf("address: %p", &b->func); 
printf("address: %p", &b->A::func);

ただし、これはコンパイルされません。実行時に vtable 内のアドレスを調べて、このようなことを行うことは可能ですか?

4

5 に答える 5

18

現在、C++ でこれを行う標準的な方法はありませんが、情報はどこかで入手できる必要があります。それ以外の場合、プログラムはどのように関数を呼び出すことができますか? ただし、GCC は、仮想関数のアドレスを取得できるようにする拡張機能を提供します。

void (A::*mfp)() = &A::func;
printf("address: %p", (void*)(b->*mfp));

...メンバー関数にプロトタイプがあると仮定しますvoid func()。これは、仮想関数のアドレスをキャッシュしたり、生成されたコードで使用したりする場合に非常に便利です。を指定しない限り、GCC はこの構成について警告します-Wno-pmf-conversions。他のコンパイラで動作する可能性は低いです。

于 2012-08-29T22:52:07.890 に答える
6

メンバー関数へのポインターは、必ずしも単純なメモリ アドレスではありません。さまざまなコンパイラでのメンバー関数ポインターのサイズを示すこの記事の表を参照してください。最大 20 バイトになるものもあります。

この記事で概説しているように、メンバー関数ポインターは実際には実装定義のデータの塊であり、ポインターを介した呼び出しを解決するのに役立ちます。保存して呼び出すことはできますが、印刷する場合は何を印刷しますか? それを一連のバイトとして扱い、 を介してその長さを取得するのが最善sizeofです。

于 2010-06-18T09:16:49.267 に答える
2

逆アセンブラ ( https://github.com/vmt/udis86 )を使用してこれを行う方法を見つけました。手順は次のとおりです。

  1. 通常の C++ コードを介して仮想関数へのポインターを取得する

  2. jmpそのアドレスの命令を逆アセンブルします

  3. 逆アセンブルされた文字列から実際のアドレスを解析する

これが私がやった方法です:

// First get the raw pointer to the virtual function
auto myVirtualFuncPtr = &MyClass::myFunc;
void* myVirtualFuncPtrRaw = (void*&)myVirtualFuncPtr;

// Resolve the real function!
void* myFuncPtr = resolveVirtualFunctionAddress(myVirtualFuncPtrRaw);

...

static void* resolveVirtualFunctionAddress(void* address)
{
    const int jumpInstructionSize = 5;

    static ud_t ud_obj;
    ud_init(&ud_obj);
    ud_set_mode(&ud_obj, sizeof(void*) * 8);
    ud_set_syntax(&ud_obj, UD_SYN_INTEL);
    ud_set_pc(&ud_obj, (uint64_t)address);
    ud_set_input_buffer(&ud_obj, (unsigned uint8_t*)address, jumpInstructionSize);

    std::string jmpInstruction = "";

    if (ud_disassemble(&ud_obj))
    {
        jmpInstruction += ud_insn_asm(&ud_obj);
    }

    // TODO: Implement startsWith and leftTrim yourself
    if (startsWith(jmpInstruction, "jmp "))
    {
        std::string jumpAddressStr = leftTrim(jmpInstruction, "jmp ");
        return hexToPointer(jumpAddressStr);
    }

    // If the jmp instruction was not found, then we just return the original address
    return address;
}

static void* hexToPointer(std::string hexString)
{
    void* address;
    std::stringstream ss;

    ss << std::hex << hexString;
    ss >> address;

    return address;
}
于 2019-02-17T02:30:08.730 に答える
1

私が標準で言えることから、動的バインディングを取得するのは、仮想関数呼び出し中だけです。そして、関数を呼び出したら、関数内のステートメントを実行します(つまり、呼び出しの途中で停止してアドレスを取得することはできません)。

不可能だと思います。

于 2010-06-18T08:51:20.357 に答える
1

私にはあまり意味がありません。通常の機能がある場合:

void f( int n ) {
}

次に、そのアドレスを取得できます。

f

しかし、関数呼び出しのアドレスを取得することはできません。これは、あなたがやりたいと思われることです。

于 2010-06-18T08:41:40.160 に答える