打印虚拟成员函数的地址

问题描述 投票:31回答:5

我正在尝试打印虚拟成员函数的地址。如果我知道哪个类实现了我可以编写的函数:

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

但我想做这样的事情:

A *b = new B();

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

但是这不会编译。是否有可能做这样的事情,也许在运行时查找vtable中的地址?

c++ virtual vtable
5个回答
13
投票

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

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

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


6
投票

指向成员函数的指针并不总是简单的内存地址。请参阅this article中的表,其中显示了不同编译器上成员函数指针的大小 - 有些指针最多可达20个字节。

正如文章概述的那样,成员函数指针实际上是一组实现定义的数据,以帮助通过指针解析调用。您可以存储并调用它们,但如果要打印它们,您打印什么?最好将其视为一个字节序列,并通过sizeof获取其长度。


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

1
投票

对我来说没有多大意义。如果你有一个正常的功能:

void f( int n ) {
}

然后你可以拿它的地址:

f

但你不能接受函数调用的地址,这是你似乎想要做的。


1
投票

从我在标准中可以看出,唯一一次获得动态绑定是在虚函数调用期间。一旦你调用了一个函数,就会执行函数中的语句(即,你不能“半途而废”进入调用并获取地址。)

我认为这是不可能的。

© www.soinside.com 2019 - 2024. All rights reserved.