Pergunta

Estou a escrever algum código do plugin em uma dll que é chamado por uma série sobre as quais não tenho controle.

O anfitrião assume que os plugins são exportados como __stdcall funções. O anfitrião é dito o nome da função e os detalhes dos argumentos que ele espera e dinamicamente Crufts uma chamada a ele através de LoadLibrary, GetProcAddress e empurrando manualmente os argumentos na pilha.

dlls Normalmente plugin de expor uma interface constante. Meu plugin expõe uma interface que está configurado na dll tempo de carregamento. Para atingir este meu plugin expõe um conjunto de pontos de entrada padrão que são definidos no momento da dll é compilado e ele aloca as quando necessário para funcionalidade interna que está sendo exposto.

Cada uma das funções internas podem assumir diferentes argumentos, mas isso é comunicado ao anfitrião junto com o nome entrypoint física. Todos os meus pontos de entrada DLL físicos são definidos para tomar um único ponteiro void * e eu organizar parâmetros subseqüentes da pilha mim mesmo por trabalhar em deslocamentos a partir do primeiro argumento e a lista de argumentos conhecido que foi comunicada ao host.

O anfitrião com êxito pode chamar as funções no meu plugin com os argumentos corretos e todas as obras bem ... No entanto, estou ciente de que: a) as minhas funções não estão limpando a pilha como eles são suposto como eles 're definido como __stdcall funções que recebem um ponteiro de 4 bytes e assim eles sempre fazem um 'ret 4' no final mesmo se o chamador tem empurrado mais argumentos na pilha. e b) Eu não posso lidar com funções que recebem sem argumentos como o ret 4 pop 4 bytes muitos fora da pilha em meu retorno.

Tendo traçado fora do meu plugin no código de chamada do hospedeiro eu posso ver que realmente a) não é esse negócio um grande; o anfitrião perde um pouco espaço de pilha até que ele retorne da chamada expedição em que ponto ele limpa o seu quadro de pilha que limpa o meu lixo; no entanto ...

posso resolver b) mudando para __cdecl e não limpeza em tudo. Eu suponho que eu posso resolver a) mudando para funções nuas e escrever meu próprio código-se limpo argumento genérico.

Desde que eu sei a quantidade de espaço argumento usado pela função que foi chamado apenas eu esperava que seria tão simples como:

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?

ATUALIZADO:

Graças às sugestões de Rob Kennedy eu tenho a este, 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                        
   }
}

Será que este olhar direito?

Foi útil?

Solução

Desde ret requer um argumento constante, você precisa mandar para sua função de ter um número constante de parâmetros, mas essa situação só é necessário no ponto em que está pronto para retornar da função. Então, pouco antes do final da função, faça o seguinte:

  1. Pop o endereço de retorno fora do topo da pilha e armazená-lo em um temporária; ECX é um bom lugar.
  2. Remova o número variável de argumentos da pilha, seja por popping cada um fora de forma individual, ou ajustando ESP diretamente.
  3. Empurre a volta endereço de retorno na pilha.
  4. Use ret com um argumento constante.

Aliás, o problema que se referem como (a) realmente é um problema, no caso geral. Você acabou de sorte que o chamador parece referir-se sempre às suas próprias variáveis ??locais usando um ponteiro quadro em vez do ponteiro de pilha. Funções não são obrigados a fazer isso, porém, e não há nenhuma garantia de que uma versão futura do programa do anfitrião continuará a trabalhar dessa forma. O compilador também é susceptível de salvar alguns valores de registo na pilha apenas para a duração da chamada, e depois esperar para ser capaz de pop-los novamente depois. Seu código iria quebrar isso.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top