Написание голых функций с собственным прологом и кодом эпилога в Visual Studio

Я пишу некоторый код плагина в dll, который вызывается хостом, который я не могу контролировать.

Хост предполагает, что плагины экспортируются как функции __stdcall. Хосту сообщается имя функции и подробности ожидаемых аргументов, и он динамически обрабатывает вызов к нему через LoadLibrary, GetProcAddress и вручную помещает аргументы в стек.

Обычно плагины библиотеки предоставляют постоянный интерфейс. Мой плагин предоставляет интерфейс, который настраивается во время загрузки DLL. Чтобы достичь этого, мой плагин предоставляет набор стандартных точек входа, которые определяются во время компиляции dll, и распределяет их по мере необходимости для раскрываемой внутренней функциональности.

Каждая из внутренних функций может принимать разные аргументы, но это передается хосту вместе с именем физической точки входа. Все мои физические точки входа в dll определены для получения одного указателя void *, и я сам собираю последующие параметры из стека, работая со смещениями из первого аргумента и списка известных аргументов, которые были переданы хосту.

Хост может успешно вызывать функции в моем плагине с правильными аргументами, и все работает хорошо ... Однако я знаю, что а) мои функции не очищают стек, как они должны, как они определены как __stdcall функции, которые принимают 4-байтовый указатель, и поэтому они всегда делают 'ret 4' в конце, даже если вызывающая сторона поместила больше аргументов в стек. и б) я не могу иметь дело с функциями, которые не принимают аргументов, так как ret 4 выкинет 4 байта слишком много из стека по моему возвращению.

Проследив мой плагин в коде вызова хоста, я вижу, что на самом деле а) не так уж и много; хост теряет некоторое место в стеке, пока не вернется из диспетчерского вызова, и в этот момент он очищает свой кадр стека, который очищает мой мусор; тем не мение...

Я могу решить б), переключившись на __cdecl и вообще не убирая. Я предполагаю, что могу решить а), переключившись на обнаженные функции и написав свой собственный общий код для очистки аргументов.

Так как я знаю объем пространства аргументов, используемого только что вызванной функцией, я надеялся, что это будет так просто:

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

Но это не работает, так как ret нуждается в постоянной времени компиляции ... Есть предложения?

ОБНОВЛЕНО:

Благодаря предложениям Роба Кеннеди я дошел до этого, который, кажется, работает ...

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

Это выглядит правильно?

Ответы на вопрос(1)

Ваш ответ на вопрос