繁体   English   中英

虚拟成员函数的打印地址

[英]Print address of virtual member function

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

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

但是我想做这样的事情:

A *b = new B();

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

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

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

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

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

指向成员函数的指针并不总是简单的内存地址。 请参阅本文中的表格,其中显示了不同编译器上的成员函数指针的大小-其中一些占20个字节。

如本文所述,成员函数指针实际上是实现定义的数据的blob,以帮助解决通过指针进行的调用。 您可以存储它们并命名为OK,但是如果要打印它们,您将打印什么? 最好将其视为字节序列,并通过sizeof获得其长度。

我找到了一种使用反汇编程序( 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;
}

对我来说没有多大意义。 如果您具有正常功能:

void f( int n ) {
}

那么您可以获取其地址:

f

但是您不能使用函数调用的地址,这似乎是您想要的。

根据我在标准中所说的,唯一获得动态绑定的时间是在虚函数调用期间。 一旦调用了函数,就可以在函数内执行语句(即,您不能“中途停止”调用并获取地址。)

我认为这是不可能的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM