Я знаю, что это обсуждалось несколько раз, но моя ситуация немного отличается.Метод динамического метода вызова C++
У меня есть сторонняя dll, экспортирующая некоторые классы. К сожалению, файл заголовка недоступен. По-прежнему можно вызвать экспортируемые функции. Но я не могу обойти передачу правильного «этого» указателя (который передается в регистре RCX).
Сначала я использую dumpbin/exports для извлечения имен функций (имена изменены, поскольку сторонняя библиотека и имена функций являются конфиденциальными).
4873 1308 0018B380 [email protected]@[email protected]@QEBAJXZ = [email protected]@[email protected]@QEBAJXZ (public: long __cdecl ThirdPartyNamespace::ThirdPartyClass::GetId(void)const)
Теперь API позволяет мне зарегистрировать свою функцию обратного вызова, которая получает указатель на ThirdPartyNamespace :: ThirdPartyClass (есть только вперед декларация ThirdPartyClass).
Вот как я пытаюсь вызвать ThirdPartyNamespace :: ThirdPartyClass :: GetId():
long (ThirdPartyNamespace::ThirdPartyClass::*_pFnGetId)() const;
HMODULE hModule = GetModuleHandle("ThirdPartyDLL.dll");
*(FARPROC*)&_pFnGetId= GetProcAddress(hModule, "[email protected]@[email protected]@QEBAJXZ");
long id = (ptr->*_pFnGetId)();
Все выглядит нормально (то есть, если я шаг в - я действительно внутри метода ThirdPartyClass :: GetId Но. этот указатель не является хорошим Хотя PTR хорошо, и если в отладчике я вручную изменить RCX на PTR - это отлично работает, но компилятор не пропускает PTR по какой-то причине Вот разборки:.
long id = (ptr->*_pFnGetId)();
000000005C882362 movsxd rax,dword ptr [rdi+30h]
000000005C882366 test eax,eax
000000005C882368 jne MyClass::MyCallback+223h (05C882373h)
000000005C88236A movsxd rcx,dword ptr [rdi+28h]
000000005C88236E add rcx,rsi
000000005C882371 jmp MyClass::MyCallback+240h (05C882390h)
000000005C882373 movsxd r8,dword ptr [rdi+2Ch]
000000005C882377 mov rcx,rax
000000005C88237A mov rax,qword ptr [r8+rsi]
000000005C88237E movsxd rdx,dword ptr [rax+rcx]
000000005C882382 movsxd rcx,dword ptr [rdi+28h]
000000005C882386 lea rax,[r8+rdx]
000000005C88238A add rcx,rax
000000005C88238D add rcx,rsi
000000005C882390 call qword ptr [rdi+20h]
000000005C882393 mov ebp,eax
Перед выполнением.. эти команды, rsi содержит указатель на объект ThirdPartyC (т. е. ptr), но вместо того, чтобы напрямую передавать его в rcx, на нем выполняется некоторая арифметика, и в результате этот указатель становится совершенно неправильным.
некоторые следы, которые я не понимаю, почему компилятор делает это, как это в конечном итоге вызова ThirdPartyClass невиртуальные функции :: GetId():
000000005C88237A mov rax,qword ptr [r8+rsi]
R8 0000000000000000
RSI 000000004C691AA0 // good pointer to ThirdPartyClass object
RAX 0000000008E87728 // this gets pointer to virtual functions table of ThirdPartyClass
000000005C88237E movsxd rdx,dword ptr [rax+rcx]
RAX 0000000008E87728
RCX FFFFFFFFFFFFFFFF
RDX FFFFFFFFC0F3C600
000000005C882382 movsxd rcx,dword ptr [rdi+28h]
RCX 0000000000000000
RDI 000000005C9BE690
000000005C882386 lea rax,[r8+rdx]
RAX FFFFFFFFC0F3C600
RDX FFFFFFFFC0F3C600
R8 0000000000000000
000000005C88238A add rcx,rax
RAX FFFFFFFFC0F3C600
RCX FFFFFFFFC0F3C600
000000005C88238D add rcx,rsi
RCX 000000000D5CE0A0
RSI 000000004C691AA0
000000005C882390 call qword ptr [rdi+20h]
На мой взгляд, это должно быть так просто, как
long id = (ptr->*_pFnGetId)();
mov rcx,rsi
call qword ptr [rdi+20h]
mov ebp,eax
И если я устанавливаю RCX равным RSi до вызова QWORD PTR [RDI + 20h] возвращает мне ожидаемое значение.
Я делаю что-то совершенно не так? Спасибо заранее.
Хороший улов. MSVC использует указатели разного размера для функций-членов в зависимости от класса, от 8 до 24 байтов на 64-битной платформе. См. Http://www.agner.org/optimize/calling_conventions.pdf –
Спасибо за ссылку! На самом деле у него есть ссылка с полным объяснением (все еще актуально с 2015 года). http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible Это действительно ум. Мне повезло, что у меня есть одно наследование в классах ThirdParty в моем случае. – Jurys