Print address of virtual member function

主宰稳场 提交于 2019-11-27 08:34:32
Lukas W

Currently there is no standard way of doing this in C++ although the information must be available somewhere. Otherwise, how could the program call the function? However, GCC provides an extension that allows us to retrieve the address of a virtual function:

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

...assuming the member function has the prototype void func(). This can be pretty useful when you want to cache the address of a virtual function or use it in generated code. GCC will warn you about this construct unless you specify -Wno-pmf-conversions. It's unlikely that it works with any other compiler.

Pointers to member functions are not always simple memory addresses. See the table in this article showing the sizes of member function pointers on different compilers - some go up to 20 bytes.

As the article outlines a member function pointer is actually a blob of implementation-defined data to help resolve a call through the pointer. You can store and call them OK, but if you want to print them, what do you print? Best to treat it as a sequence of bytes and get its length via sizeof.

I found a way to do this using a disassembler (https://github.com/vmt/udis86). The steps are:

  1. Get a pointer to the virtual function via normal C++ code

  2. Disassemble the jmp instruction at that address

  3. Parse the real address from the disassembled string

Here is how I did it:

// 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;
}

Doesn't make a lot a of sense to me. If you have a normal function:

void f( int n ) {
}

then you can take its address:

f

but you cannot take the address of a function call, which is what you seem to want to do.

From what I can tell in the standard, the only time you get dynamic binding is during a virtual function call. And once you've called a function, you're executing the statements within the function (i.e., you can't "stop halfway" into the call and get the address.)

I think it's impossible.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!