Domanda

Sto scrivendo un po 'codice del plugin in una DLL che viene chiamato da un host su cui non ho alcun controllo.

L'host presuppone che i plugin sono esportate come __stdcall funzioni. L'host è detto il nome della funzione ei dettagli degli argomenti che si prevede e Crufts dinamicamente una chiamata ad esso tramite LoadLibrary, GetProcAddress e spingendo manualmente gli argomenti nello stack.

Di solito plug DLL espongono un'interfaccia costante. La mia plug espone un'interfaccia che si configura al momento del caricamento dll. Per raggiungere questo obiettivo il mio plug espone una serie di punti di ingresso standard che vengono definiti al momento della DLL viene compilata e assegna loro come necessario per la funzionalità interna che viene esposto.

Ciascuna delle funzioni interne possono assumere diverse argomentazioni ma questo viene comunicato all'host insieme al nome entrypoint fisico. Tutti i miei entrypoints dll fisici sono definiti a prendere un singolo puntatore void * e io maresciallo parametri successivi dallo stack stesso, lavorando da offset dal primo argomento e il conosciuto lista di argomenti che è stato comunicato al host.

L'host può con successo richiamare le funzioni nel mio plugin con gli argomenti corretti e tutto funziona bene ... Tuttavia, sono consapevole del fatto che a) le mie funzioni non sono la pulizia della pila come si suppone che in quanto 're definito come __stdcall funzioni che richiedono un puntatore a 4 byte e quindi fanno sempre un 'ret 4' al termine anche se il chiamante ha spinto più argomenti nello stack. e b) non riesco a fare con le funzioni che accettano senza argomenti come il ret 4 si aprirà 4 byte troppi fuori della pila al mio ritorno.

Dopo aver tracciato dal mio plug-in codice chiamante del padrone di casa mi rendo conto che in realtà a) non è un grosso problema; l'ospite perde un po 'di spazio di stack fino a tornare dalla chiamata dispaccio a quel punto pulisce il suo stack frame che pulisce la mia spazzatura; però ...

posso risolvere b) passando a __cdecl e non ripulire affatto. Presumo che posso risolvere un) passando a funzioni nudi e scrivere il mio codice di ripulire argomento generico.

Dal momento che so la quantità di spazio argomento utilizzato dalla funzione che è stato appena chiamato avevo sperato che sarebbe stato così semplice come:

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
   }
}

Ma questo non funziona come ret ha bisogno di un tempo di compilazione costante ... Qualche suggerimento?

AGGIORNAMENTO:

Grazie ai suggerimenti di Rob Kennedy Ho avuto modo di questo, che sembra funzionare ...

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                        
   }
}

Questo sembra giusto?

È stato utile?

Soluzione

Dal ret richiede un argomento costante, è necessario organizzare per la vostra funzione di avere un numero costante di parametri, ma questa situazione è necessaria solo nel punto si è pronti per tornare dalla funzione. Così, poco prima della fine della funzione, fare questo:

  1. Pop l'indirizzo di ritorno dalla cima della pila e conservarla in una temporanea; ECX è un buon posto.
  2. Rimuovere il numero variabile di argomenti dalla pila, sia estraendo ciascuno off singolarmente, o regolando direttamente ESP.
  3. Inserire l'indirizzo di ritorno di nuovo in cima alla pila.
  4. Usa ret con un argomento costante.

Per inciso, la questione si fa riferimento a come (a) è davvero un problema, nel caso generale. Hai appena stato fortunato che il chiamante sembra riferirsi sempre alle proprie variabili locali che utilizzano una forma di cornice invece che lo stack pointer. Le funzioni non sono tenuti a fare questo, però, e non c'è alcuna garanzia che una futura versione del programma host continuerà a lavorare in questo modo. Il compilatore è anche suscettibile di salvare alcuni valori di registro nello stack solo per la durata della chiamata, e poi si aspettano di essere in grado di farli scoppiare di nuovo in seguito. Il tuo codice sarebbe rompere questo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top