Click here to Skip to main content
15,886,806 members
Articles / Programming Languages / MSIL

.NET Internals and Code Injection

Rate me:
Please Sign up or sign in to vote.
4.91/5 (53 votes)
14 May 2008CPOL24 min read 144K   5.2K   188  
An article about .NET internals and code injection
// ==++==
//
//   
//    Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
//   
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//   
//    You must not remove this notice, or any other, from this software.
//   
//
// ==--==
/*****************************************************************************\
*                                                                             *
* CorJit.h -    EE / JIT interface                                            *
*                                                                             *
*               Version 1.0                                                   *
*******************************************************************************
*                                                                             *
*                                                                              
                                                                              *
*                                                                             *
\*****************************************************************************/

#ifndef _COR_JIT_H_
#define _COR_JIT_H_

#include "corinfo.h"

/* The default max method size that the inliner considers for inlining */
#define DEFAULT_INLINE_SIZE             32

#define CORINFO_STACKPROBE_DEPTH        256*sizeof(UINT_PTR)          // Guaranteed stack until an fcall/unmanaged
                                                    // code can set up a frame. Please make sure
                                                    // this is less than a page. This is due to
                                                    // 2 reasons:
                                                    //
                                                    // If we need to probe more than a page
                                                    // size, we need one instruction per page
                                                    // (7 bytes per instruction)
                                                    //
                                                    // The JIT wants some safe space so it doesn't
                                                    // have to put a probe on every call site. It achieves
                                                    // this by probing n bytes more than CORINFO_STACKPROBE_DEPTH
                                                    // If it hasn't used more than n for its own stuff, it
                                                    // can do a call without doing any other probe
                                                    //
                                                    // In any case, we do really expect this define to be
                                                    // small, as setting up a frame should be only pushing
                                                    // a couple of bytes on the stack
                                                    //
                                                    // There is a compile time assert
                                                    // in the x86 jit to protect you from this
                                                    //




/*****************************************************************************/
    // These are error codes returned by CompileMethod
enum CorJitResult
{
    // Note that I dont use FACILITY_NULL for the facility number,
    // we may want to get a 'real' facility number
    CORJIT_OK            =     NO_ERROR,
    CORJIT_BADCODE       =     MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 1),
    CORJIT_OUTOFMEM      =     MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 2),
    CORJIT_INTERNALERROR =     MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 3),
    CORJIT_SKIPPED       =     MAKE_HRESULT(SEVERITY_ERROR,FACILITY_NULL, 4),
};

/* values for flags in compileMethod */

enum CorJitFlag
{
    CORJIT_FLG_SPEED_OPT           = 0x00000001,
    CORJIT_FLG_SIZE_OPT            = 0x00000002,
    CORJIT_FLG_DEBUG_CODE          = 0x00000004, // generate "debuggable" code (no code-mangling optimizations)
    CORJIT_FLG_DEBUG_EnC           = 0x00000008, // We are in Edit-n-Continue mode
    CORJIT_FLG_DEBUG_INFO          = 0x00000010, // generate line and local-var info
    CORJIT_FLG_LOOSE_EXCEPT_ORDER  = 0x00000020, // loose exception order
#if defined(_X86_)
    CORJIT_FLG_TARGET_PENTIUM      = 0x00000100,
    CORJIT_FLG_TARGET_PPRO         = 0x00000200,
    CORJIT_FLG_TARGET_P4           = 0x00000400,
    CORJIT_FLG_TARGET_BANIAS       = 0x00000800,
    CORJIT_FLG_USE_FCOMI           = 0x00001000, // Generated code may use fcomi(p) instruction
    CORJIT_FLG_USE_CMOV            = 0x00002000, // Generated code may use cmov instruction
    CORJIT_FLG_USE_SSE2            = 0x00004000, // Generated code may use SSE-2 instructions
#endif
    CORJIT_FLG_PROF_CALLRET        = 0x00010000, // Wrap method calls with probes
    CORJIT_FLG_PROF_ENTERLEAVE     = 0x00020000, // Instrument prologues/epilogues
    CORJIT_FLG_PROF_INPROC_ACTIVE_DEPRECATED    = 0x00040000, // Inprocess debugging active requires different instrumentation
    CORJIT_FLG_PROF_NO_PINVOKE_INLINE           = 0x00080000, // Disables PInvoke inlining
    CORJIT_FLG_SKIP_VERIFICATION   = 0x00100000, // (lazy) skip verification - determined without doing a full resolve. See comment below
    CORJIT_FLG_PREJIT              = 0x00200000, // jit or prejit is the execution engine.
    CORJIT_FLG_RELOC               = 0x00400000, // Generate relocatable code
    CORJIT_FLG_IMPORT_ONLY         = 0x00800000, // Only import the function
    CORJIT_FLG_IL_STUB             = 0x01000000, // method is an IL stub
    CORJIT_FLG_PROCSPLIT           = 0x02000000, // JIT should separate code into hot and cold sections
    CORJIT_FLG_BBINSTR             = 0x04000000, // Collect basic block profile information
    CORJIT_FLG_BBOPT               = 0x08000000, // Optimize method based on profile information
    CORJIT_FLG_FRAMED              = 0x10000000, // All methods have an EBP frame
    CORJIT_FLG_ALIGN_LOOPS         = 0x20000000, // add NOPs before loops to align them at 16 byte boundaries
    CORJIT_FLG_PUBLISH_SECRET_PARAM= 0x40000000, // JIT must place stub secret param into local 0.  (used by IL stubs)
};

/*****************************************************************************
Here is how CORJIT_FLG_SKIP_VERIFICATION should be interepreted.
Note that even if any method is inlined, it need not be verified.

if (CORJIT_FLG_SKIP_VERIFICATION is passed in to ICorJitCompiler::compileMethod())
{
    No verification needs to be done.
    Just compile the method, generating unverifiable code if necessary
}
else
{
    switch(ICorMethodInfo::isInstantiationOfVerifiedGeneric())
    {
    case INSTVER_NOT_INSTANTIATION:

        //
        // Non-generic case, or open generic instantiation
        //

        switch(canSkipMethodVerification())
        {
        case CORINFO_VERIFICATION_CANNOT_SKIP:
            {
                ICorMethodInfo::initConstraintsForVerification(&circularConstraints)
                if (circularConstraints)
                {
                    Just emit code to call CORINFO_HELP_VERIFICATION
                    The IL will not be compiled
                }
                else
                {
                    Verify the method.
                    if (unverifiable code is detected)
                    {
                        In place of branches with unverifiable code, emit code to call CORINFO_HELP_VERIFICATION
                        Mark the method (and any of its instantiations) as unverifiable
                    }
                    Compile the rest of the verifiable code
                }
            }

        case CORINFO_VERIFICATION_CAN_SKIP:
            {
                No verification needs to be done.
                Just compile the method, generating unverifiable code if necessary
            }

        case CORINFO_VERIFICATION_RUNTIME_CHECK:
            {
                ICorMethodInfo::initConstraintsForVerification(&circularConstraints)
                if (circularConstraints)
                {
                    Just emit code to call CORINFO_HELP_VERIFICATION
                    The IL will not be compiled

                    TODO: This could be changed to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK
                }
                else
                {
                    Verify the method.
                    if (unverifiable code is detected)
                    {
                        In the prolog, emit code to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK
                        Mark the method (and any of its instantiations) as unverifiable
                    }
                    Compile the method, generating unverifiable code if necessary
                }
            }
        }

    case INSTVER_GENERIC_PASSED_VERIFICATION:
        {
            This cannot ever happen because the VM would pass in CORJIT_FLG_SKIP_VERIFICATION.
        }

    case INSTVER_GENERIC_FAILED_VERIFICATION:

        switch(canSkipMethodVerification())
        {
            case CORINFO_VERIFICATION_CANNOT_SKIP:
                {
                    This cannot be supported because the compiler does not know which branches should call CORINFO_HELP_VERIFICATION.
                    The CLR will throw a VerificationException instead of trying to compile this method
                }

            case CORINFO_VERIFICATION_CAN_SKIP:
                {
                    This cannot ever happen because the CLR would pass in CORJIT_FLG_SKIP_VERIFICATION.
                }

            case CORINFO_VERIFICATION_RUNTIME_CHECK:
                {
                    No verification needs to be done.
                    In the prolog, emit code to call CORINFO_HELP_VERIFICATION_RUNTIME_CHECK
                    Compile the method, generating unverifiable code if necessary
                }
        }
    }
}

*/

/*****************************************************************************/
// These are flags passed to ICorJitInfo::allocMem
// to guide the memory allocation for the code, readonly data, and read-write data
enum CorJitAllocMemFlag
{
    CORJIT_ALLOCMEM_FLG_16BYTE_ALIGN  = 0x00000001, // Force the code to be 16-byte aligned
};

class ICorJitCompiler;
class ICorJitInfo;

struct IEEMemoryManager;

extern "C" ICorJitCompiler* __stdcall getJit();

/*******************************************************************************
 * ICorJitCompiler is the interface that the EE uses to get IL byteocode converted
 * to native code.  Note that to accomplish this the JIT has to call back to the
 * EE to get symbolic information.  The IJitInfo passed to the compileMethod
 * routine is the handle the JIT uses to call back to the EE
 *******************************************************************************/

class ICorJitCompiler
{
public:
    virtual CorJitResult __stdcall compileMethod (
            ICorJitInfo                 *comp,               /* IN */
            struct CORINFO_METHOD_INFO  *info,               /* IN */
            unsigned /* CorJitFlag */   flags,               /* IN */
            BYTE                        **nativeEntry,       /* OUT */
            ULONG                       *nativeSizeOfCode    /* OUT */
            ) = 0;

    virtual void __stdcall clearCache() = 0;
    virtual BOOL __stdcall isCacheCleanupRequired() = 0;
};

/*********************************************************************************
 * a ICorJitInfo is the main interface that the JIT uses to call back to the EE and
 *   get information
 *********************************************************************************/

class ICorJitInfo : public virtual ICorDynamicInfo
{
public:
    // return memory manager that the JIT can use to allocate a regular memory
    virtual IEEMemoryManager* __stdcall getMemoryManager() = 0;

    // get a block of memory for the code, readonly data, and read-write data
    virtual void __stdcall allocMem (
            ULONG               hotCodeSize,    /* IN */
            ULONG               coldCodeSize,   /* IN */
            ULONG               roDataSize,     /* IN */
            ULONG               rwDataSize,     /* IN */
            ULONG               xcptnsCount,    /* IN */
            CorJitAllocMemFlag  flag,           /* IN */
            void **             hotCodeBlock,   /* OUT */
            void **             coldCodeBlock,  /* OUT */
            void **             roDataBlock,    /* OUT */
            void **             rwDataBlock     /* OUT */
            ) = 0;


        // Get a block of memory needed for the code manager information,
        // (the info for enumerating the GC pointers while crawling the
        // stack frame).
        // Note that allocMem must be called first
    virtual void * __stdcall allocGCInfo (
            ULONG                    size        /* IN */
            ) = 0;

    virtual void * __stdcall getEHInfo(
            ) = 0;

    virtual void __stdcall yieldExecution() = 0;

	// indicate how many exception handlers blocks are to be returned
	// this is guarenteed to be called before any 'setEHinfo' call.
	// Note that allocMem must be called before this method can be called
    virtual void __stdcall setEHcount (
            unsigned		     cEH    /* IN */
            ) = 0;

    // set the values for one particular exception handler block
    //
    // Handler regions should be lexically contiguous.
    // This is because FinallyIsUnwinding() uses lexicality to
    // determine if a "finally" clause is executing
    virtual void __stdcall setEHinfo (
            unsigned		     EHnumber,   /* IN  */
            const CORINFO_EH_CLAUSE *clause      /* IN */
            ) = 0;

    // Level -> fatalError, Level 2 -> Error, Level 3 -> Warning
    // Level 4 means happens 10 times in a run, level 5 means 100, level 6 means 1000 ...
    // returns non-zero if the logging succeeded
    virtual BOOL __cdecl logMsg(unsigned level, const char* fmt, va_list args) = 0;

    // do an assert.  will return true if the code should retry (DebugBreak)
    // returns false, if the assert should be igored.
    virtual int __stdcall doAssert(const char* szFile, int iLine, const char* szExpr) = 0;

    struct ProfileBuffer
    {
        ULONG bbOffset;
        ULONG bbCount;
    };

    // allocate a basic block profile buffer where execution counts will be stored
    // for jitted basic blocks.
    virtual HRESULT __stdcall allocBBProfileBuffer (
            ULONG                 size,
            ProfileBuffer **      profileBuffer
            ) = 0;

    // get profile information to be used for optimizing the current method.  The format
    // of the buffer is the same as the format the JIT passes to allocBBProfileBuffer.
    virtual HRESULT __stdcall getBBProfileData(
            CORINFO_METHOD_HANDLE ftnHnd,
            ULONG *               size,
            ProfileBuffer **      profileBuffer,
            ULONG *               numRuns
            ) = 0;


};

/**********************************************************************************/
#endif // _COR_CORJIT_H_

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
The languages I know best are: C, C++, C#, Assembly (x86, x64, ARM), MSIL, Python, Lua. The environments I frequently use are: Qt, Win32, MFC, .NET, WDK. I'm a developer and a reverse engineer and I like playing around with internals.

You can find most of my work at http://ntcore.com.

Comments and Discussions