Can't hook ICorJitCompiler:compileMethod from Managed Code

Oct 18, 2012 at 9:48 PM
Edited Oct 19, 2012 at 8:11 PM

I've been trying for long now to hook ICorJitCompiler:compileMethod from Managed Code in v4.0 using EasyHook LocalHook.Create. I've obtained the function pointer from unmarshalling an structure like this:

 

public static class NativeJitInterop
{
    [DllImport("clrjit.dll", CharSet=CharSet.None, SetLastError = true)]
    private static extern IntPtr getJit();

    public static ClrJitCompilerHook GetManagedJitCompiler()
    {
        ClrJitCompilerHook clrJitCompiler = null;

        IntPtr _clrJitPtr = getJit();
            
        ICorJitCompiler _corJitCompiler = (ICorJitCompiler) Marshal.PtrToStructure(_clrJitPtr, typeof(ICorJitCompiler));
        clrJitCompiler = new ClrJitCompilerHook(_clrJitPtr, _corJitCompiler.compileMethod);

        return clrJitCompiler;
    }    
 }

    [StructLayout(LayoutKind.Sequential)]
    internal struct ICorJitCompiler
    {
        [MarshalAs(UnmanagedType.FunctionPtr)]
        public CompileMethodSig compileMethod;

    }

    [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
    internal delegate Int32 CompileMethodSig(IntPtr thisPtr, IntPtr corJitInfo, IntPtr methodInfo, UInt32 flags, [Out] IntPtr ILCode, [Out] UInt64 ILCodeSize);

 

Everything works fine, the structure seems to be unmarshalled without problems and the contained delegate's _methodPtr and _methodPtrAux fields are fill with some pointer values.

The problem arises when I try to set the hook like this:

 

public void HookClrJitCompiler()
{
	IntPtr _compileMethodPtr = Marshal.GetFunctionPointerForDelegate(_compileMethodDelegate);

        _localJitHook = LocalHook.Create(_compileMethodPtr, new CompileMethodSig(ClrJitCompilerCalled), this);

        _localJitHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
}

 

I obtain and AccessViolationException.

I worked this around and set  _compileMethodPtr variable to delegate's _methodPtr. I got no exceptions when cretaing the hook but the hook didn't work either.

what i'm doing wrong?

Oct 20, 2012 at 4:56 AM

Hi,

I usually get the address of interface methods using something like the following:

        protected IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods)
        {
            List<IntPtr> vtblAddresses = new List<IntPtr>();

            IntPtr vTable = Marshal.ReadIntPtr(pointer);
            for (int i = 0; i < numberOfMethods; i++)
                vtblAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size)); // using IntPtr.Size allows us to support both 32 and 64-bit processes

            return vtblAddresses.ToArray();
        }

Where you pass in the JIT instance, and say you want 1 method (i.e. you are only after the first one on the ICorJitCompiler).

That's the only thing I can think of - I will try this some time soon for the JIT stuff.

Cheers,

Oct 20, 2012 at 5:51 AM

Also take a look at this codeproject article (using managed C++). The main part of interest is how he gets the address.

Oct 22, 2012 at 2:22 PM
Edited Oct 22, 2012 at 2:24 PM

Hey, thanks a lot. It worked like a charm. After this correction I was able to effectively hook ICorJitCompiler:compileMethod. I saw the VTbl solution in a DirectX article you published in your blog but I was not really sure how it works. I've also seen the CodeProject article  -it's my inspiration actually- but its approach is a bit tricky using .pdb files, hash functions and embedded resources and that's the reason why I was looking a more direct and managed way.  

Here is the full code in case you ever need it.

As you may deduct, now I'm trying to obtain some info about the method it's being compiled through corJitInfo pointer. Wish me luck :-).

 

Thanks again,

Manfred.

 

    public static class NativeJitInterop
    {
        [DllImport("clrjit.dll", SetLastError = true, PreserveSig=true, CallingConvention=CallingConvention.StdCall)]
        private static extern IntPtr getJit();

        public static ClrJitCompilerHook GetManagedJitCompiler()
        {
            ClrJitCompilerHook clrJitCompiler = null;

            IntPtr _clrJitPtr = getJit();

            IntPtr _jitCompileMethodPtr = GetVTblAddresses(_clrJitPtr, 1).First();

            clrJitCompiler = new ClrJitCompilerHook(_clrJitPtr, _jitCompileMethodPtr);
            
            return clrJitCompiler;
        }
        public static IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods) 
        { 
            List<IntPtr> vtblAddresses = new List<IntPtr>();  
            IntPtr vTable = Marshal.ReadIntPtr(pointer); 
            for (int i = 0; i < numberOfMethods; i++) 
                vtblAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size)); // using IntPtr.Size allows us to support both 32 and 64-bit
            return vtblAddresses.ToArray(); 
        }
    }

    [UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
    public delegate UInt32 CompileMethodSig([In] IntPtr corJitInfo, [In] IntPtr methodInfo, CorJitFlag flags, [Out] IntPtr ILCode, [Out] UInt64 ILCodeSize);

    public class ClrJitCompilerHook
    {
        private IntPtr _clrJitPointer;
        private IntPtr _compileMethodPtr;
        private LocalHook _localJitHook;
        private CompileMethodSig _hookMethod;

        internal ClrJitCompilerHook(IntPtr clrJitPtr, IntPtr compileMethodPtr)
        {
            _clrJitPointer = clrJitPtr;
            _compileMethodPtr = compileMethodPtr;            
            _hookMethod = new CompileMethodSig(ClrJitCompilerCalled);
        }
        public ClrJitCompilerHook()
        {

        }
        protected virtual void OnJitCompileCalled(MethodInfo methodInfo)
        { 
            
        }

        internal UInt32 ClrJitCompilerCalled(IntPtr corJitInfo, IntPtr corMethodInfo, CorJitFlag flags, IntPtr ILCode, UInt64 ILCodeSize)
        {

            CorMethodInfo _clrMethodInfo = (CorMethodInfo)Marshal.PtrToStructure(corMethodInfo, typeof(CorMethodInfo));
                       
            CompileMethodSig jitCompileMethod = (CompileMethodSig)Marshal.GetDelegateForFunctionPointer(_compileMethodPtr, typeof(CompileMethodSig));
            
            UInt32 result = jitCompileMethod(corJitInfo, corMethodInfo, flags, ILCode, ILCodeSize);

            return result;
        }

        public void HookClrJitCompiler()
        {
            _localJitHook = LocalHook.Create(_compileMethodPtr, _hookMethod , this);

            _localJitHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });

        }
    }

    [StructLayout(LayoutKind.Sequential, Pack=1, Size=0x88)]    
    public struct CorMethodInfo
    {
        public IntPtr methodHandle;
        public IntPtr moduleHandle;
        public IntPtr ilCode;
        public UInt32 ilCodeSize;
        public UInt16 maxStack;
        public UInt16 EHCount;
        public UInt32 corInfoOptions;
    }
Oct 23, 2012 at 9:16 AM

Good to hear, and thanks for posting what worked.

Good luck

Oct 23, 2012 at 1:49 PM

Actually, I've tested it a bit more and realized that ICorJitCompiler:compileMethod  receive as a first parameter a this pointer. So now, CompileMethodSig delegate looks like this:

[UnmanagedFunctionPointer(CallingConvention.StdCall, SetLastError = true)]
    public delegate UInt32 CompileMethodSig(IntPtr thisPtr, [In] IntPtr corJitInfo, [In] IntPtr methodInfo, CorJitFlag flags, [Out] IntPtr ILCode, [Out] IntPtr ILCodeSize);

Cheers,

Manfred