Распечатать адрес функции виртуального члена - PullRequest
31 голосов
/ 18 июня 2010

Я пытаюсь распечатать адрес функции виртуального члена. Если я знаю, какой класс реализует функцию, я могу написать:

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

Но я хочу сделать что-то вроде этого:

A *b = new B();

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

Однако это не компилируется. Можно ли сделать что-то подобное, возможно, поиск адреса в vtable во время выполнения?

Ответы [ 5 ]

13 голосов
/ 30 августа 2012

В настоящее время не существует стандартного способа сделать это в C ++, хотя информация должна быть где-то доступна.Иначе, как программа может вызвать функцию?Однако GCC предоставляет расширение, которое позволяет нам получить адрес виртуальной функции:

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

... при условии, что функция-член имеет прототип void func().Это может быть очень полезно, когда вы хотите кэшировать адрес виртуальной функции или использовать его в сгенерированном коде.GCC будет предупреждать вас об этой конструкции, если вы не укажете -Wno-pmf-conversions.Маловероятно, что он работает с любым другим компилятором.

6 голосов
/ 18 июня 2010

Указатели на функции-члены не всегда являются простыми адресами памяти.См. Таблицу в этой статье , показывающую размеры указателей на функции-члены на разных компиляторах - некоторые достигают 20 байтов.

Как показано в статье, указатель на функцию-член на самом деле является большим количеством реализации-определенные данные, чтобы помочь разрешить звонок через указатель.Вы можете хранить и называть их ОК, но если вы хотите распечатать их, что вы печатаете?Лучше всего рассматривать его как последовательность байтов и получить его длину через sizeof.

2 голосов
/ 17 февраля 2019

Я нашел способ сделать это с помощью дизассемблера (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 голос
/ 18 июня 2010

Из того, что я могу сказать в стандарте, вы получаете динамическое связывание только во время виртуальной функции call . И как только вы вызвали функцию, вы выполняете операторы внутри функции (то есть вы не можете «остановиться на полпути» в вызове и получить адрес.)

Я думаю, что это невозможно.

1 голос
/ 18 июня 2010

Не имеет большого смысла для меня.Если у вас есть нормальная функция:

void f( int n ) {
}

, тогда вы можете взять ее адрес:

f

, но вы не можете взять адрес функции call , котораяэто то, что вы, кажется, хотите сделать.

...