Frage

Ich schreibe einen Plugin-Code in einer DLL, die von einem Host, über die ich keine Kontrolle habe genannt wird.

Der Host geht davon aus, dass der Plugins als __stdcall Funktionen exportiert werden. Der Host ist der Name der Funktion erzählt und die Details der Argumente, dass es erwartet und dynamisch crufts eines Anrufs, um es über Loadlibrary, GetProcAddress und manuell die Argumente auf den Stapel schieben.

In der Regel Plugin DLLs eine konstante Schnittstelle aus. Mein Plugin macht eine Schnittstelle, die bei dll Ladezeit konfiguriert ist. Um dies zu erreichen mein Plugin stellt eine Reihe von Standard-Einstiegspunkte, die zu dem Zeitpunkt definiert werden die DLL kompiliert und es ordnet sie nach Bedarf interne Funktionen, die ausgesetzt ist wird.

Jeder der internen Funktionen kann unterschiedliche Argumente aber zusammen mit den physischen Einstiegspunkt Namen an den Host übermittelt wird. Alle meine physischen dll Einstiegspunkte definiert einen einzelnen void * Zeiger zu nehmen und ich nachfolgende Parameter vom Stack selbst Marschall von von Versetzungen aus dem ersten Argument und dem bekannten Argument-Liste arbeiten, die an den Host übermittelt wurde.

Der Host kann erfolgreich mit den richtigen Argumenten, die Funktionen in meinem Plugin anrufen und alles funktioniert gut ... Aber ich bewusst bin, dass a) meine Funktionen nicht den Stapel Reinigung werden, wie sie als angeblich sind sie ist als __stdcall Funktionen definiert, die einen 4-Byte-Zeiger nehmen und so tun sie immer eine ‚ret 4‘ am Ende, auch wenn der Anrufer mehr Argumente auf den Stapel geschoben hat. und b) Ich kann nicht mit Funktionen umgehen, die keine Argumente als ret nehmen 4 erscheinen 4 Bytes zu viele von dem Stapel auf meiner Rückkehr.

Nachdem aus meiner Plugin in die Host-Aufrufcode zurückverfolgt ich, dass tatsächlich a) sehen kann, ist nicht so große Sache; der Host verliert einige Stapelspeicher, bis er aus dem Dispatch-Anruf, an dem wieder zeigen sie ihren Stapelrahmen bereinigt, die mein Müll reinigt; aber ...

Ich kann b) lösen durch Umschalten haupt __cdecl und nicht aufzuräumen. Ich nehme an, ich kann eine Lösung) durch die bloße Funktion Schalten und meinen eigenen generisches Argument aufzuräumen Code zu schreiben.

Da ich weiß, die Menge Argumentationsraum durch die Funktion, die gerade genannt wurde ich hatte gehofft, dass es so einfach sein würde, wie:

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

Aber das funktioniert nicht wie ret eine Kompilierung konstant ... Irgendwelche Vorschläge braucht?

AKTUALISIERT:

Dank Rob Kennedy Vorschläge, die ich dazu habe, das scheint zu funktionieren ...

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

Ist dies richtig aussehen?

War es hilfreich?

Lösung

Da ret ein konstantes Argument erfordert, müssen Sie arrangieren für Ihre Funktion eine konstante Anzahl von Parametern zu haben, aber diese Situation nur an der Stelle erforderlich ist, Sie bereit sind, aus der Funktion zurück. Also, kurz vor dem Ende der Funktion, dies zu tun:

  1. Pop die Rücksprungadresse auf der Oberseite des Stapels und speichern sie in einem temporären; ECX ist ein guter Ort.
  2. Entfernen Sie die variable Anzahl von Argumenten aus dem Stapel, entweder durch diese einzeln Abspringen oder durch ESP direkt eingestellt werden.
  3. Drücken Sie die Absenderadresse zurück auf den Stapel.
  4. Verwenden Sie ret mit einem konstanten Argument.

Im übrigen ist die Frage, die Sie beziehen als (a) ist wirklich ein Problem, im allgemeinen Fall. Sie haben gerade das Glück, dass der Anrufer immer scheint seine eigene lokale Variablen zu verweisen einen Rahmenzeiger anstelle der Stapelzeiger verwendet wird. Die Funktionen sind allerdings nicht das zu tun, erforderlich ist, und es gibt keine Garantie, dass eine zukünftige Version des Host-Programms wird auch weiterhin auf diese Weise arbeiten. Der Compiler haftet auch einige Registerwerte auf dem Stapel nur für die Dauer des Anrufs zu speichern, und dann erwarten, dass sie später wieder abspringt. Der Code würde das brechen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top