// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
//*****************************************************************************
//
// EETwain.h
//
// This file has the definition of ICodeManager and EECodeManager.
//
// ICorJitCompiler compiles the IL of a method to native code, and stores
// auxilliary data called as GCInfo (via ICorJitInfo::allocGCInfo()).
// The data is used by the EE to manage the method's garbage collection,
// exception handling, stack-walking etc.
// This data can be parsed by an ICodeManager corresponding to that
// ICorJitCompiler.
//
// EECodeManager is an implementation of ICodeManager for a default format
// of GCInfo. Various ICorJitCompiler's are free to share this format so that
// they do not need to provide their own implementation of ICodeManager
// (though they are permitted to, if they want).
//
//*****************************************************************************

#ifndef _EETWAIN_H
#define _EETWAIN_H
//*****************************************************************************

#include <daccess.h>
#include "regdisp.h"
#include "corjit.h"     // For NativeVarInfo
#include "stackwalktypes.h"
#include "bitvector.h"
#include "gcinfotypes.h"

#if !defined(TARGET_X86)
#define USE_GC_INFO_DECODER
#endif

#if (defined(TARGET_X86) && !defined(TARGET_UNIX)) || defined(TARGET_AMD64)
#define HAS_QUICKUNWIND
#endif

#define CHECK_APP_DOMAIN    0

#define NO_OVERRIDE_OFFSET (DWORD)-1

struct EHContext;

#ifdef DACCESS_COMPILE
typedef struct _DAC_SLOT_LOCATION
{
    int reg;
    int regOffset;
    bool targetPtr;

    _DAC_SLOT_LOCATION(int _reg, int _regOffset, bool _targetPtr)
        : reg(_reg), regOffset(_regOffset), targetPtr(_targetPtr)
    {
    }
} DacSlotLocation;
#endif

typedef void (*GCEnumCallback)(
    LPVOID          hCallback,      // callback data
    OBJECTREF*      pObject,        // address of object-reference we are reporting
    uint32_t        flags           // is this a pinned and/or interior pointer
    DAC_ARG(DacSlotLocation loc)    // where the reference came from
);

/******************************************************************************
  The stackwalker maintains some state on behalf of ICodeManager.
*/

const int CODEMAN_STATE_SIZE = 512;

struct CodeManState
{
    DWORD       dwIsSet; // Is set to 0 by the stackwalk as appropriate
    BYTE        stateBuf[CODEMAN_STATE_SIZE];
};

/******************************************************************************
   These flags are used by some functions, although not all combinations might
   make sense for all functions.
*/

enum ICodeManagerFlags
{
    ActiveStackFrame =  0x0001, // this is the currently active function
    ExecutionAborted =  0x0002, // execution of this function has been aborted
                                    // (i.e. it will not continue execution at the
                                    // current location)
    AbortingCall    =   0x0004, // The current call will never return
    UpdateAllRegs   =   0x0008, // update full register set
    CodeAltered     =   0x0010, // code of that function might be altered
                                    // (e.g. by debugger), need to call EE
                                    // for original code
    SpeculativeStackwalk
                    =   0x0020, // we're in the middle of a stackwalk seeded
                                    // by an untrusted source (e.g., sampling profiler)

    ParentOfFuncletStackFrame
                    =   0x0040, // A funclet for this frame was previously reported
    NoReportUntracked
                    =   0x0080, // EnumGCRefs/EnumerateLiveSlots should *not* include
                                // any untracked slots
};

//*****************************************************************************
//
// EECodeInfo is used by ICodeManager to get information about the
// method whose GCInfo is being processed.
// It is useful so that some information which is available elsewhere does
// not need to be cached in the GCInfo.
//

class EECodeInfo;

enum GenericParamContextType
{
    GENERIC_PARAM_CONTEXT_NONE = 0,
    GENERIC_PARAM_CONTEXT_THIS = 1,
    GENERIC_PARAM_CONTEXT_METHODDESC = 2,
    GENERIC_PARAM_CONTEXT_METHODTABLE = 3
};

//*****************************************************************************
//
// ICodeManager is the abstract class that all CodeManagers
// must inherit from.  This will probably need to move into
// cor.h and become a real com interface.
//
//*****************************************************************************

class ICodeManager
{
    VPTR_BASE_VTABLE_CLASS_AND_CTOR(ICodeManager)

public:

/*
    Last chance for the runtime support to do fixups in the context
    before execution continues inside a filter, catch handler, or fault/finally
*/

enum ContextType
{
    FILTER_CONTEXT,
    CATCH_CONTEXT,
    FINALLY_CONTEXT
};

/* Type of funclet corresponding to a shadow stack-pointer */

enum
{
    SHADOW_SP_IN_FILTER = 0x1,
    SHADOW_SP_FILTER_DONE = 0x2,
    SHADOW_SP_BITS = 0x3
};

#ifndef DACCESS_COMPILE
#ifndef FEATURE_EH_FUNCLETS
virtual void FixContext(ContextType     ctxType,
                        EHContext      *ctx,
                        EECodeInfo     *pCodeInfo,
                        DWORD           dwRelOffset,
                        DWORD           nestingLevel,
                        OBJECTREF       thrownObject,
                        CodeManState   *pState,
                        size_t       ** ppShadowSP,             // OUT
                        size_t       ** ppEndRegion) = 0;       // OUT
#endif // !FEATURE_EH_FUNCLETS
#endif // #ifndef DACCESS_COMPILE

#ifdef TARGET_X86
/*
    Gets the ambient stack pointer value at the given nesting level within
    the method.
*/
virtual TADDR GetAmbientSP(PREGDISPLAY     pContext,
                           EECodeInfo     *pCodeInfo,
                           DWORD           dwRelOffset,
                           DWORD           nestingLevel,
                           CodeManState   *pState) = 0;
#endif // TARGET_X86

/*
    Get the number of bytes used for stack parameters.
    This is currently only used on x86.
*/
virtual ULONG32 GetStackParameterSize(EECodeInfo* pCodeInfo) = 0;

/*
    Unwind the current stack frame, i.e. update the virtual register
    set in pContext. This will be similar to the state after the function
    returns back to caller (IP points to after the call, Frame and Stack
    pointer has been reset, callee-saved registers restored
    (if UpdateAllRegs), callee-UNsaved registers are trashed)
    Returns success of operation.
*/
virtual bool UnwindStackFrame(PREGDISPLAY     pContext,
                              EECodeInfo     *pCodeInfo,
                              unsigned        flags,
                              CodeManState   *pState,
                              StackwalkCacheUnwindInfo  *pUnwindInfo) = 0;

/*
    Is the function currently at a "GC safe point" ?
    Can call EnumGcRefs() successfully
*/
virtual bool IsGcSafe(EECodeInfo     *pCodeInfo,
                      DWORD           dwRelOffset) = 0;

#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
virtual bool HasTailCalls(EECodeInfo *pCodeInfo) = 0;
#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64

#if defined(TARGET_AMD64) && defined(_DEBUG)
/*
    Locates the end of the last interruptible region in the given code range.
    Returns 0 if the entire range is uninterruptible.  Returns the end point
    if the entire range is interruptible.
*/
virtual unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
                                                  unsigned endOffset,
                                                  GCInfoToken gcInfoToken) = 0;
#endif // TARGET_AMD64 && _DEBUG

/*
    Enumerate all live object references in that function using
    the virtual register set. Same reference location cannot be enumerated
    multiple times (but all differenct references pointing to the same
    object have to be individually enumerated).
    Returns success of operation.
*/
virtual bool EnumGcRefs(PREGDISPLAY     pContext,
                        EECodeInfo     *pCodeInfo,
                        unsigned        flags,
                        GCEnumCallback  pCallback,
                        LPVOID          hCallBack,
                        DWORD           relOffsetOverride = NO_OVERRIDE_OFFSET) = 0;

/*
    For a non-static method, "this" pointer is passed in as argument 0.
    However, if there is a "ldarga 0" or "starg 0" in the IL,
    JIT will create a copy of arg0 and redirect all "ldarg(a) 0" and "starg 0" to this copy.
    (See Compiler::lvaArg0Var for more details.)

    The following method returns the original "this" argument, i.e. the one that is passed in,
    if it is a non-static method AND the object is still alive.
    Returns NULL in all other cases.
*/
virtual OBJECTREF GetInstance(PREGDISPLAY     pContext,
                              EECodeInfo*     pCodeInfo) = 0;

/*
    Returns the extra argument passed to to shared generic code if it is still alive.
    Returns NULL in all other cases.
*/
virtual PTR_VOID GetParamTypeArg(PREGDISPLAY     pContext,
                                 EECodeInfo *    pCodeInfo) = 0;

// Returns the type of the context parameter (this, methodtable, methoddesc, or none)
virtual GenericParamContextType GetParamContextType(PREGDISPLAY     pContext,
                                                    EECodeInfo *    pCodeInfo) = 0;

/*
    Returns the offset of the GuardStack cookie if it exists.
    Returns NULL if there is no cookie.
*/
virtual void * GetGSCookieAddr(PREGDISPLAY     pContext,
                               EECodeInfo    * pCodeInfo,
                               CodeManState  * pState) = 0;

#ifndef USE_GC_INFO_DECODER
/*
  Returns true if the given IP is in the given method's prolog or an epilog.
*/
virtual bool IsInPrologOrEpilog(DWORD  relPCOffset,
                                GCInfoToken gcInfoToken,
                                size_t* prologSize) = 0;

/*
  Returns true if the given IP is in the synchronized region of the method (valid for synchronized methods only)
*/
virtual bool IsInSynchronizedRegion(
                DWORD       relOffset,
                GCInfoToken gcInfoToken,
                unsigned    flags) = 0;
#endif // !USE_GC_INFO_DECODER

/*
  Returns the size of a given function as reported in the GC info (does
  not take procedure splitting into account).  For the actual size of
  the hot region call IJitManager::JitTokenToMethodHotSize.
*/
virtual size_t GetFunctionSize(GCInfoToken gcInfoToken) = 0;

/*
*  Get information necessary for return address hijacking of the method represented by the gcInfoToken.
*  If it can be hijacked, it sets the returnKind output parameter to the kind of the return value and
*  returns true.
*  If hijacking is not possible for some reason, it return false.
*/
virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind) = 0;

#ifndef USE_GC_INFO_DECODER
/*
  Returns the size of the frame (barring localloc)
*/
virtual unsigned int GetFrameSize(GCInfoToken gcInfoToken) = 0;
#endif // USE_GC_INFO_DECODER

#ifndef DACCESS_COMPILE

/* Debugger API */

#ifndef FEATURE_EH_FUNCLETS
virtual const BYTE*     GetFinallyReturnAddr(PREGDISPLAY pReg)=0;

virtual BOOL            IsInFilter(GCInfoToken gcInfoToken,
                                   unsigned offset,
                                   PCONTEXT pCtx,
                                   DWORD curNestLevel) = 0;

virtual BOOL            LeaveFinally(GCInfoToken gcInfoToken,
                                     unsigned offset,
                                     PCONTEXT pCtx) = 0;

virtual void            LeaveCatch(GCInfoToken gcInfoToken,
                                   unsigned offset,
                                   PCONTEXT pCtx)=0;
#endif // FEATURE_EH_FUNCLETS

#ifdef EnC_SUPPORTED

/*
    Last chance for the runtime support to do fixups in the context
    before execution continues inside an EnC updated function.
*/

virtual HRESULT FixContextForEnC(PCONTEXT        pCtx,
                                    EECodeInfo    * pOldCodeInfo,
               const ICorDebugInfo::NativeVarInfo * oldMethodVars,
                                    SIZE_T          oldMethodVarsCount,
                                    EECodeInfo    * pNewCodeInfo,
               const ICorDebugInfo::NativeVarInfo * newMethodVars,
                                    SIZE_T          newMethodVarsCount) = 0;

#endif // EnC_SUPPORTED

#endif // #ifndef DACCESS_COMPILE


#ifdef DACCESS_COMPILE
    virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags) = 0;
#endif
};

//*****************************************************************************
//
// EECodeManager is the EE's implementation of the ICodeManager which
// supports the default format of GCInfo.
//
//*****************************************************************************

struct hdrInfo;

class EECodeManager : public ICodeManager {

    VPTR_VTABLE_CLASS_AND_CTOR(EECodeManager, ICodeManager)

public:


#ifndef DACCESS_COMPILE
#ifndef FEATURE_EH_FUNCLETS
/*
    Last chance for the runtime support to do fixups in the context
    before execution continues inside a filter, catch handler, or finally
*/
virtual
void FixContext(ContextType     ctxType,
                EHContext      *ctx,
                EECodeInfo     *pCodeInfo,
                DWORD           dwRelOffset,
                DWORD           nestingLevel,
                OBJECTREF       thrownObject,
                CodeManState   *pState,
                size_t       ** ppShadowSP,             // OUT
                size_t       ** ppEndRegion);           // OUT
#endif // !FEATURE_EH_FUNCLETS
#endif // #ifndef DACCESS_COMPILE

#ifdef TARGET_X86
/*
    Gets the ambient stack pointer value at the given nesting level within
    the method.
*/
virtual
TADDR GetAmbientSP(PREGDISPLAY     pContext,
                   EECodeInfo     *pCodeInfo,
                   DWORD           dwRelOffset,
                   DWORD           nestingLevel,
                   CodeManState   *pState);
#endif // TARGET_X86

/*
    Get the number of bytes used for stack parameters.
    This is currently only used on x86.
*/
virtual
ULONG32 GetStackParameterSize(EECodeInfo* pCodeInfo);

/*
    Unwind the current stack frame, i.e. update the virtual register
    set in pContext. This will be similar to the state after the function
    returns back to caller (IP points to after the call, Frame and Stack
    pointer has been reset, callee-saved registers restored
    (if UpdateAllRegs), callee-UNsaved registers are trashed)
    Returns success of operation.
*/
virtual
bool UnwindStackFrame(
                PREGDISPLAY     pContext,
                EECodeInfo     *pCodeInfo,
                unsigned        flags,
                CodeManState   *pState,
                StackwalkCacheUnwindInfo  *pUnwindInfo);

#ifdef HAS_QUICKUNWIND
enum QuickUnwindFlag
{
    UnwindCurrentStackFrame,
    EnsureCallerStackFrameIsValid
};

/*
  *  Light unwind the current stack frame, using provided cache entry.
  *  only pPC and Esp of pContext are updated. And pEbp if necessary.
  */

static
void QuickUnwindStackFrame(
             PREGDISPLAY pRD,
             StackwalkCacheEntry *pCacheEntry,
             QuickUnwindFlag flag);
#endif // HAS_QUICKUNWIND

/*
    Is the function currently at a "GC safe point" ?
    Can call EnumGcRefs() successfully
*/
virtual
bool IsGcSafe(  EECodeInfo     *pCodeInfo,
                DWORD           dwRelOffset);

#if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64)
virtual
bool HasTailCalls(EECodeInfo *pCodeInfo);
#endif // TARGET_ARM || TARGET_ARM64 || TARGET_LOONGARCH64

#if defined(TARGET_AMD64) && defined(_DEBUG)
/*
    Locates the end of the last interruptible region in the given code range.
    Returns 0 if the entire range is uninterruptible.  Returns the end point
    if the entire range is interruptible.
*/
virtual
unsigned FindEndOfLastInterruptibleRegion(unsigned curOffset,
                                          unsigned endOffset,
                                          GCInfoToken gcInfoToken);
#endif // TARGET_AMD64 && _DEBUG

/*
    Enumerate all live object references in that function using
    the virtual register set. Same reference location cannot be enumerated
    multiple times (but all differenct references pointing to the same
    object have to be individually enumerated).
    Returns success of operation.
*/
virtual
bool EnumGcRefs(PREGDISPLAY     pContext,
                EECodeInfo     *pCodeInfo,
                unsigned        flags,
                GCEnumCallback  pCallback,
                LPVOID          hCallBack,
                DWORD           relOffsetOverride = NO_OVERRIDE_OFFSET);

#ifdef FEATURE_CONSERVATIVE_GC
// Temporary conservative collection, for testing purposes, until we have
// accurate gc info from the JIT.
bool EnumGcRefsConservative(PREGDISPLAY     pRD,
                            EECodeInfo     *pCodeInfo,
                            unsigned        flags,
                            GCEnumCallback  pCallBack,
                            LPVOID          hCallBack);
#endif // FEATURE_CONSERVATIVE_GC

virtual
OBJECTREF GetInstance(
                PREGDISPLAY     pContext,
                EECodeInfo *    pCodeInfo);

/*
    Returns the extra argument passed to to shared generic code if it is still alive.
    Returns NULL in all other cases.
*/
virtual
PTR_VOID GetParamTypeArg(PREGDISPLAY     pContext,
                         EECodeInfo *    pCodeInfo);

// Returns the type of the context parameter (this, methodtable, methoddesc, or none)
virtual GenericParamContextType GetParamContextType(PREGDISPLAY     pContext,
                                                    EECodeInfo *    pCodeInfo);

#if defined(FEATURE_EH_FUNCLETS) && defined(USE_GC_INFO_DECODER)
/*
    Returns the generics token.  This is used by GetInstance() and GetParamTypeArg() on WIN64.
*/
static
PTR_VOID GetExactGenericsToken(PREGDISPLAY     pContext,
                               EECodeInfo *    pCodeInfo);

static
PTR_VOID GetExactGenericsToken(SIZE_T          baseStackSlot,
                               EECodeInfo *    pCodeInfo);


#endif // FEATURE_EH_FUNCLETS && USE_GC_INFO_DECODER

/*
    Returns the offset of the GuardStack cookie if it exists.
    Returns NULL if there is no cookie.
*/
virtual
void * GetGSCookieAddr(PREGDISPLAY     pContext,
                       EECodeInfo    * pCodeInfo,
                       CodeManState  * pState);


#ifndef USE_GC_INFO_DECODER
/*
  Returns true if the given IP is in the given method's prolog or an epilog.
*/
virtual
bool IsInPrologOrEpilog(
                DWORD       relOffset,
                GCInfoToken gcInfoToken,
                size_t*     prologSize);

/*
  Returns true if the given IP is in the synchronized region of the method (valid for synchronized functions only)
*/
virtual
bool IsInSynchronizedRegion(
                DWORD       relOffset,
                GCInfoToken gcInfoToken,
                unsigned    flags);
#endif // !USE_GC_INFO_DECODER

/*
  Returns the size of a given function.
*/
virtual
size_t GetFunctionSize(GCInfoToken gcInfoToken);

/*
*  Get information necessary for return address hijacking of the method represented by the gcInfoToken.
*  If it can be hijacked, it sets the returnKind output parameter to the kind of the return value and
*  returns true.
*  If hijacking is not possible for some reason, it return false.
*/
virtual bool GetReturnAddressHijackInfo(GCInfoToken gcInfoToken, ReturnKind * returnKind);

#ifndef USE_GC_INFO_DECODER
/*
  Returns the size of the frame (barring localloc)
*/
virtual
unsigned int GetFrameSize(GCInfoToken gcInfoToken);
#endif // USE_GC_INFO_DECODER

#ifndef DACCESS_COMPILE

#ifndef FEATURE_EH_FUNCLETS
virtual const BYTE* GetFinallyReturnAddr(PREGDISPLAY pReg);
virtual BOOL IsInFilter(GCInfoToken gcInfoToken,
                        unsigned offset,
                        PCONTEXT pCtx,
                          DWORD curNestLevel);
virtual BOOL LeaveFinally(GCInfoToken gcInfoToken,
                          unsigned offset,
                          PCONTEXT pCtx);
virtual void LeaveCatch(GCInfoToken gcInfoToken,
                         unsigned offset,
                         PCONTEXT pCtx);
#endif // FEATURE_EH_FUNCLETS

#ifdef EnC_SUPPORTED
/*
    Last chance for the runtime support to do fixups in the context
    before execution continues inside an EnC updated function.
*/
virtual
HRESULT FixContextForEnC(PCONTEXT        pCtx,
                            EECodeInfo    * pOldCodeInfo,
       const ICorDebugInfo::NativeVarInfo * oldMethodVars,
                            SIZE_T          oldMethodVarsCount,
                            EECodeInfo    * pNewCodeInfo,
       const ICorDebugInfo::NativeVarInfo * newMethodVars,
                            SIZE_T          newMethodVarsCount);
#endif // EnC_SUPPORTED

#endif // #ifndef DACCESS_COMPILE

#ifdef FEATURE_EH_FUNCLETS
    static void EnsureCallerContextIsValid( PREGDISPLAY pRD, StackwalkCacheEntry* pCacheEntry, EECodeInfo * pCodeInfo = NULL );
    static size_t GetCallerSp( PREGDISPLAY  pRD );
#ifdef TARGET_X86
    static size_t GetResumeSp( PCONTEXT  pContext );
#endif // TARGET_X86
#endif // FEATURE_EH_FUNCLETS

#ifdef DACCESS_COMPILE
    virtual void EnumMemoryRegions(CLRDataEnumMemoryFlags flags);
#endif

};

#ifdef TARGET_X86
bool UnwindStackFrame(PREGDISPLAY     pContext,
                      EECodeInfo     *pCodeInfo,
                      unsigned        flags,
                      CodeManState   *pState,
                      StackwalkCacheUnwindInfo  *pUnwindInfo);

size_t DecodeGCHdrInfo(GCInfoToken gcInfoToken,
                       unsigned    curOffset,
                       hdrInfo   * infoPtr);
#endif

/*****************************************************************************
 <TODO>ToDo: Do we want to include JIT/IL/target.h? </TODO>
 */

enum regNum
{
        REGI_EAX, REGI_ECX, REGI_EDX, REGI_EBX,
        REGI_ESP, REGI_EBP, REGI_ESI, REGI_EDI,
        REGI_COUNT,
        REGI_NA = REGI_COUNT
};

/*****************************************************************************
 Register masks
 */

enum RegMask
{
    RM_EAX = 0x01,
    RM_ECX = 0x02,
    RM_EDX = 0x04,
    RM_EBX = 0x08,
    RM_ESP = 0x10,
    RM_EBP = 0x20,
    RM_ESI = 0x40,
    RM_EDI = 0x80,

    RM_NONE = 0x00,
    RM_ALL = (RM_EAX|RM_ECX|RM_EDX|RM_EBX|RM_ESP|RM_EBP|RM_ESI|RM_EDI),
    RM_CALLEE_SAVED = (RM_EBP|RM_EBX|RM_ESI|RM_EDI),
    RM_CALLEE_TRASHED = (RM_ALL & ~RM_CALLEE_SAVED),
};

/*****************************************************************************
 *
 *  Helper to extract basic info from a method info block.
 */

struct hdrInfo
{
    unsigned int        methodSize;     // native code bytes
    unsigned int        argSize;        // in bytes
    unsigned int        stackSize;      // including callee saved registers
    unsigned int        rawStkSize;     // excluding callee saved registers
    ReturnKind          returnKind;     // The ReturnKind for this method.

    unsigned int        prologSize;

    // Size of the epilogs in the method.
    // For methods which use CEE_JMP, some epilogs may end with a "ret" instruction
    // and some may end with a "jmp". The epilogSize reported should be for the
    // epilog with the smallest size.
    unsigned int        epilogSize;

    unsigned char       epilogCnt;
    bool                epilogEnd;      // is the epilog at the end of the method

    bool                ebpFrame;       // locals and arguments addressed relative to EBP
    bool                doubleAlign;    // is the stack double-aligned? locals addressed relative to ESP, and arguments relative to EBP
    bool                interruptible;  // intr. at all times (excluding prolog/epilog), not just call sites

    bool                securityCheck;  // has a slot for security object
    bool                handlers;       // has callable handlers
    bool                localloc;       // uses localloc
    bool                editNcontinue;  // has been compiled in EnC mode
    bool                varargs;        // is this a varargs routine
    bool                profCallbacks;  // does the method have Enter-Leave callbacks
    bool                genericsContext;// has a reported generic context parameter
    bool                genericsContextIsMethodDesc;// reported generic context parameter is methoddesc
    bool                isSpeculativeStackWalk; // is the stackwalk seeded by an untrusted source (e.g., sampling profiler)?

    // These always includes EBP for EBP-frames and double-aligned-frames
    RegMask             savedRegMask:8; // which callee-saved regs are saved on stack

    // Count of the callee-saved registers, excluding the frame pointer.
    // This does not include EBP for EBP-frames and double-aligned-frames.
    unsigned int        savedRegsCountExclFP;

    unsigned int        untrackedCnt;
    unsigned int        varPtrTableSize;
    unsigned int        argTabOffset;   // INVALID_ARGTAB_OFFSET if argtab must be reached by stepping through ptr tables
    unsigned int        gsCookieOffset; // INVALID_GS_COOKIE_OFFSET if there is no GuardStack cookie

    unsigned int        syncStartOffset; // start/end code offset of the protected region in synchronized methods.
    unsigned int        syncEndOffset;   // INVALID_SYNC_OFFSET if there not synchronized method
    unsigned int        syncEpilogStart; // The start of the epilog. Synchronized methods are guaranteed to have no more than one epilog.
    unsigned int        revPInvokeOffset; // INVALID_REV_PINVOKE_OFFSET if there is no Reverse PInvoke frame

    enum { NOT_IN_PROLOG = -1, NOT_IN_EPILOG = -1 };

    int                 prologOffs;     // NOT_IN_PROLOG if not in prolog
    int                 epilogOffs;     // NOT_IN_EPILOG if not in epilog. It is never 0

    //
    // Results passed back from scanArgRegTable
    //
    regNum              thisPtrResult;  // register holding "this"
    RegMask             regMaskResult;  // registers currently holding GC ptrs
    RegMask            iregMaskResult;  // iptr qualifier for regMaskResult
    unsigned            argHnumResult;
    PTR_CBYTE            argTabResult;  // Table of encoded offsets of pending ptr args
    unsigned              argTabBytes;  // Number of bytes in argTabResult[]

    // These next two are now large structs (i.e 132 bytes each)

    ptrArgTP            argMaskResult;  // pending arguments mask
    ptrArgTP           iargMaskResult;  // iptr qualifier for argMaskResult
};

/*****************************************************************************
  How the stackwalkers buffer will be interpreted
*/

struct CodeManStateBuf
{
    DWORD       hdrInfoSize;
    hdrInfo     hdrInfoBody;
};
//*****************************************************************************
#endif // _EETWAIN_H
//*****************************************************************************
