Escrever funções nuas com código de prólogo e epilog personalizado no Visual Studio
Estou escrevendo algum código de plug-in em uma dll chamada por um host sobre o qual não tenho control
O host assume que os plugins são exportados como funções __stdcall. O host recebe o nome da função e os detalhes dos argumentos que espera e cria uma chamada dinamicamente para ele via LoadLibrary, GetProcAddress e manualmente pressionando os argumentos na pilh
ormalmente, as DLLs de plug-in expõem uma interface constante. Meu plug-in expõe uma interface configurada no tempo de carregamento da DLL. Para isso, meu plug-in expõe um conjunto de pontos de entrada padrão definidos no momento em que a dll é compilada e os aloca conforme necessário para a funcionalidade interna que está sendo expost
Cada uma das funções internas pode receber argumentos diferentes, mas isso é comunicado ao host junto com o nome do ponto de entrada físico. Todos os meus pontos de entrada da dll física são definidos para receber um único ponteiro nulo * e eu organizo os parâmetros subsequentes da pilha, trabalhando com as compensações do primeiro argumento e a lista de argumentos conhecidos que foi comunicada ao hos
O host pode chamar com êxito as funções no meu plug-in com os argumentos corretos e tudo funciona bem ... No entanto, eu sei que a) minhas funções não estão limpando a pilha como deveriam, como deveriam definido como funções __stdcall que usam um ponteiro de 4 bytes e, portanto, sempre fazem um 'ret 4' no final, mesmo que o chamador tenha empurrado mais argumentos para a pilha. eb) não consigo lidar com funções que não recebem argumentos, pois os ret 4 reterão 4 bytes demais da pilha no meu retorn
Tendo traçado fora do meu plugin no código de chamada do host, posso ver que, na verdade, a) não é grande coisa; o host perde um pouco de espaço na pilha até retornar da chamada de despacho; nesse momento, limpa seu quadro de pilha, o que limpa meu lixo; Contudo..
Eu posso resolver b) mudando para __cdecl e não limpando nada. Suponho que posso resolver a) alternando para funções simples e escrevendo meu próprio código genérico de limpeza de argument
Como eu sei a quantidade de espaço de argumento usada pela função que acabamos de chamar, esperava que fosse tão simples quanto:
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
size_t argumentSpaceUsed;
{
void *pX = RealEntryPoint(
reinterpret_cast<ULONG_PTR>(&pArg1),
argumentSpaceUsed);
__asm
{
mov eax, dword ptr pX
}
}
__asm
{
ret argumentSpaceUsed
}
}
Mas isso não funciona como ret precisa de uma constante de tempo de compilação ... Alguma sugestão?
ATUALIZADA
Graças às sugestões de Rob Kennedy, cheguei a isso, que parece funcionar ...
extern "C" __declspec(naked) __declspec(dllexport) void * __stdcall EntryPoint(void *pArg1)
{
__asm {
push ebp // Set up our stack frame
mov ebp, esp
mov eax, 0x0 // Space for called func to return arg space used, init to 0
push eax // Set up stack for call to real Entry point
push esp
lea eax, pArg1
push eax
call RealEntryPoint // result is left in eax, we leave it there for our caller....
pop ecx
mov esp,ebp // remove our stack frame
pop ebp
pop edx // return address off
add esp, ecx // remove 'x' bytes of caller args
push edx // return address back on
ret
}
}
Isso parece certo?