Скажем, у вас есть класс С++:
class Foo {
public:
virtual ~Foo() {}
virtual DoSomething() = 0;
};
Компилятор С++ переводит вызов в поиск vtable:
Foo* foo;
// Translated by C++ to:
// foo->vtable->DoSomething(foo);
foo->DoSomething();
Предположим, что я писал компилятор JIT, и я хотел получить адрес функции DoSomething() для конкретного экземпляра класса Foo, поэтому я могу сгенерировать код, который перескакивает непосредственно, вместо того, чтобы выполнять поиск в таблице и косвенную ветвь.
Мои вопросы:
-
Есть ли какой-либо стандартный способ С++ для этого (я почти уверен, что ответ отрицательный, но хотелось попросить о полноте).
-
Есть ли какой-либо отдаленный независимый от компилятора способ сделать это, как библиотека, которую кто-то реализовал, которая предоставляет API для доступа к vtable?
Я полностью открыт для взлома, если они будут работать. Например, если я создал свой собственный производный класс и смог определить адрес его метода DoSomething, я мог бы предположить, что vtable является первым (скрытым) членом Foo и выполняет поиск по его таблице vtable, пока не найду значение моего указателя. Тем не менее, я не знаю способ получить этот адрес: если я пишу &DerivedFoo::DoSomething
, я получаю указатель на элемент, что совершенно другое.
Возможно, я мог бы превратить указатель в элемент в смещение vtable. Когда я скомпилирую следующее:
class Foo {
public:
virtual ~Foo() {}
virtual void DoSomething() = 0;
};
void foo(Foo *f, void (Foo::*member)()) {
(f->*member)();
}
В GCC/x86-64 я получаю этот вывод сборки:
Disassembly of section .text:
0000000000000000 <_Z3fooP3FooMS_FvvE>:
0: 40 f6 c6 01 test sil,0x1
4: 48 89 74 24 e8 mov QWORD PTR [rsp-0x18],rsi
9: 48 89 54 24 f0 mov QWORD PTR [rsp-0x10],rdx
e: 74 10 je 20 <_Z3fooP3FooMS_FvvE+0x20>
10: 48 01 d7 add rdi,rdx
13: 48 8b 07 mov rax,QWORD PTR [rdi]
16: 48 8b 74 30 ff mov rsi,QWORD PTR [rax+rsi*1-0x1]
1b: ff e6 jmp rsi
1d: 0f 1f 00 nop DWORD PTR [rax]
20: 48 01 d7 add rdi,rdx
23: ff e6 jmp rsi
Я не совсем понимаю, что происходит здесь, но если бы я мог перепроектировать это или использовать спецификацию ABI, я мог бы создать фрагмент, подобный выше для каждой отдельной платформы, в качестве способа получения указателя из виртуальные таблицы.