c++ 虚拟成员函数的打印地址

3mpgtkmj  于 2023-03-25  发布在  其他
关注(0)|答案(5)|浏览(159)

我试图打印一个虚成员函数的地址。如果我知道哪个类实现了这个函数,我可以写:

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

但我想做这样的事情:

A *b = new B();

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

但是这并不能编译。有没有可能做类似的事情,比如在运行时在vtable中查找地址?

v7pvogib

v7pvogib1#

目前在C++中还没有标准的方法来做到这一点,尽管这些信息必须在某个地方可用。否则,程序如何调用函数?然而,GCC提供了一个扩展,允许我们检索虚函数的地址:

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

...假设成员函数的原型是void func()。当你想要缓存一个虚函数的地址或者在生成的代码中使用它时,这会非常有用。GCC会警告你这个构造,除非你指定-Wno-pmf-conversions。它不太可能在任何其他编译器上工作。

p4rjhz4m

p4rjhz4m2#

指向成员函数的指针并不总是简单的内存地址。参见this article中的表,该表显示了不同编译器上成员函数指针的大小-有些高达20字节。
正如本文所述,成员函数指针实际上是一个实现定义的数据blob,用于帮助通过指针解析调用。您可以存储和调用它们,但如果您想打印它们,您打印什么?最好将其视为字节序列并通过sizeof获取其长度。

lrl1mhuk

lrl1mhuk3#

我找到了一种使用反汇编器(https://github.com/vmt/udis86)来实现这一点的方法。步骤如下:
1.通过普通C++代码获取虚函数指针
1.在该地址反汇编jmp指令
1.从反汇编的字符串中解析出真实的地址
我是这样做的:

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

mf98qq944#

从我所知道的标准中,你唯一得到动态绑定的时候是在一个虚函数 * 调用 * 的时候。一旦你调用了一个函数,你就在执行函数中的语句(也就是说,你不能“中途停止”进入调用并获取地址。)
我认为这是不可能的。

wlzqhblo

wlzqhblo5#

对我来说没什么意义。如果你有正常的功能:

void f( int n ) {
}

然后你可以把它的地址:

f

但是你不能获取函数call的地址,而这正是你想要做的。

相关问题