/*
   p_x86.c   - __penter for x86/x86em (Windows CE)

   Can also be used for Win32 builds if you want to use the same analysis
   tools.

   $Revision: 1.2 $ $Date: 2002/04/15 23:43:16 $ $Author: Stephane $
*/

#if !defined( _UNDER_CE )
#  define PROFILE_WITH_EXCEPTIONS
#else
#  undef PROFILE_WITH_EXCEPTIONS
#endif

/* Defined in profiler.c */
#if defined( PROFILE_WITH_EXCEPTIONS )
extern void __stdcall __handle_penter(unsigned long dwAddr,
                                      unsigned long dwAddr2,
                                      unsigned long dwParentFrame);
extern unsigned long __stdcall __handle_return(unsigned long dwParentFrame);
#else
extern void __stdcall __handle_penter(unsigned long dwAddr,
                                      unsigned long dwAddr2);
extern unsigned long __stdcall __handle_return(void);
#endif

__declspec(naked) void __cdecl _pexit(void) {
   /*
      NOTE: This function is not CALL'ed, it's RET'ed to; so that's why we
            need to add an extra push 0 to fake being called...
   */
   _asm {
      push  0                 // Insert placeholder for new return address
      push  eax               // Save current eax

#if defined( PROFILE_WITH_EXCEPTIONS )
      // Pass parent's frame address
      push  ebp
#endif

      call __handle_return

      // eax now contains the return address where we must go; we just have
      // to return where our caller thought he was going, and it should be OK

      mov   dword ptr [esp+4], eax

      pop   eax

      ret   0
   }
}

/*
   Called from every function entry point when program is compiled with /Gh.

   ClientFunc PROC NEAR
      call __penter
      push ebp
      mov  ebp, esp
      pop  ebp
      ret  0
   ClientFunc  ENDP
*/

__declspec(naked) void __cdecl _penter(void) {
   /*
      [ebp+4]  ; return address of _penter    --> profiler entry point (roughly)
      [ebp+8]  ; return address of ClientFunc --> change to _pexit
   */
   _asm {
      // Faster to push just what we need than using pushad
      // ecx and edx are not restored by __handle_penter for some reason;
      // ecx is often the "this" pointer in C++...
      push  ebp
      mov   ebp, esp
      push  eax
      push  ecx
      push  edx

#if defined( PROFILE_WITH_EXCEPTIONS )
      // Parent's stack frame; used to get out of a jam when exceptions are
      // thrown.
      mov   eax, dword ptr [ebp]
      push  eax
#endif

      // 2nd argument to __handle_penter: The original return address.
      mov   eax, dword ptr [ebp+8]
      push  eax

      // Modify ClientFunc's return address to go to _pexit
      lea   eax, _pexit
      mov   dword ptr [ebp+8], eax

      // 1st argument to __handle_penter: approx. ClientFunc's start address
      // (it's actually the next instruction -- if you want to real address,
      // you can do "sub eax, 5" before pushing eax; -- doing it this way lets
      // the Perl script fix it up and we save a microscopic amount of time
      // here)
      mov   eax, dword ptr [ebp+4]
      push  eax

      call  __handle_penter

      pop   edx
      pop   ecx
      pop   eax
      pop   ebp
      ret   0
   }
}


