Skip to content

Invalid IL exception with Mono and .NET Framework #230

@Meivyn

Description

@Meivyn

Description

Changing IL instructions with ILCursor throws an InvalidProgramException when the hook is applied. This issue was introduced since 93a3d9d. Using net472 and Mono from Unity 2022.3.33f1. Does not throw when running from the CoreCLR runtime.

System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition:DMD<DMD<>?953292825::ConsoleApp5.Program::Original> (): IL_0076: call      0x0000001d
  at (wrapper managed-to-native) System.RuntimeMethodHandle.GetFunctionPointer(intptr)
  at System.RuntimeMethodHandle.GetFunctionPointer () [0x00000] in <812f0de03a50406495fcf89f8084dfd4>:0 
  at MonoMod.Core.Platforms.Runtimes.MonoRuntime.Compile (System.Reflection.MethodBase method) [0x00008] in <c8544a96d826479bab87939de32bc508>:0 
  at MonoMod.Core.Platforms.PlatformTriple.Compile (System.Reflection.MethodBase method) [0x00047] in <c8544a96d826479bab87939de32bc508>:0 
  at MonoMod.RuntimeDetour.DetourManager+ManagedDetourState.UpdateEndOfChain () [0x000e1] in <e0f4e58211844eec912c979914e6364b>:0 
  at MonoMod.RuntimeDetour.DetourManager+ManagedDetourState.AddILHook (MonoMod.RuntimeDetour.DetourManager+SingleILHookState ilhook, System.Boolean takeLock) [0x000e9] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook.Apply () [0x00052] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase method, MonoMod.Cil.ILContext+Manipulator manipulator, MonoMod.Core.IDetourFactory factory, MonoMod.RuntimeDetour.DetourConfig config, System.Boolean applyByDefault) [0x00098] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip, MonoMod.RuntimeDetour.DetourConfig config, System.Boolean applyByDefault) [0x00008] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip, MonoMod.RuntimeDetour.DetourConfig config) [0x00000] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip) [0x00008] in <e0f4e58211844eec912c979914e6364b>:0
  at ConsoleApp5.Program.Main (System.String[] args) [0x00055] in <f214ce8aacc74f19b7bc22b2b72acef0>:0
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition:DMD<DMD<>?953292825::ConsoleApp5.Program::Original> (): IL_0076: call      0x0000001d
  at (wrapper managed-to-native) System.RuntimeMethodHandle.GetFunctionPointer(intptr)
  at System.RuntimeMethodHandle.GetFunctionPointer () [0x00000] in <812f0de03a50406495fcf89f8084dfd4>:0
  at MonoMod.Core.Platforms.Runtimes.MonoRuntime.Compile (System.Reflection.MethodBase method) [0x00008] in <c8544a96d826479bab87939de32bc508>:0
  at MonoMod.Core.Platforms.PlatformTriple.Compile (System.Reflection.MethodBase method) [0x00047] in <c8544a96d826479bab87939de32bc508>:0 
  at MonoMod.RuntimeDetour.DetourManager+ManagedDetourState.UpdateEndOfChain () [0x000e1] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.DetourManager+ManagedDetourState.AddILHook (MonoMod.RuntimeDetour.DetourManager+SingleILHookState ilhook, System.Boolean takeLock) [0x000e9] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook.Apply () [0x00052] in <e0f4e58211844eec912c979914e6364b>:0 
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase method, MonoMod.Cil.ILContext+Manipulator manipulator, MonoMod.Core.IDetourFactory factory, MonoMod.RuntimeDetour.DetourConfig config, System.Boolean applyByDefault) [0x00098] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip, MonoMod.RuntimeDetour.DetourConfig config, System.Boolean applyByDefault) [0x00008] in <e0f4e58211844eec912c979914e6364b>:0 
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip, MonoMod.RuntimeDetour.DetourConfig config) [0x00000] in <e0f4e58211844eec912c979914e6364b>:0
  at MonoMod.RuntimeDetour.ILHook..ctor (System.Reflection.MethodBase source, MonoMod.Cil.ILContext+Manipulator manip) [0x00008] in <e0f4e58211844eec912c979914e6364b>:0
  at ConsoleApp5.Program.Main (System.String[] args) [0x00055] in <f214ce8aacc74f19b7bc22b2b72acef0>:0 

Example

using System.Reflection;
using System.Runtime.CompilerServices;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;

namespace ConsoleApp5
{

    internal class Program
    {
        private class SomeType
        {
            public int index { get; set; }
        }

        private static readonly List<SomeType>[] _list = new List<SomeType>[3];

        private static void Main(string[] args)
        {
            for (var i = 0; i < 3; i++)
            {
                _list[i] = new List<SomeType>(3);
            }

            var original = typeof(Program).GetMethod(nameof(Original), BindingFlags.Static | BindingFlags.NonPublic);
            new ILHook(original, Hook1).Apply();
            new ILHook(original, Hook2).Apply();
        }

        private static void Original()
        {
            var random = new Random();
            var enumerable = Enumerable.Range(0, 50).Select(i => new SomeType { index = random.Next(0, 3) });

            foreach (var item in enumerable)
            {
                _ = _list[item.index];
            }
        }

        private static void Hook1(ILContext il)
        {
            var cursor = new ILCursor(il);

            cursor.GotoNext(
                MoveType.After,
                x => x.Match(OpCodes.Ldloc_2),
                x => x.Match(OpCodes.Callvirt),
                x => x.Match(OpCodes.Ldelem_Ref));

            cursor.Index--;

            cursor.Emit(OpCodes.Ldc_I4_0);
            cursor.Emit(OpCodes.Ldc_I4_3);
            cursor.EmitCall(typeof(Program).GetMethod(nameof(Clamp), new[] { typeof(int), typeof(int), typeof(int) }));
        }

        private static void Hook2(ILContext il)
        {
            var cursor = new ILCursor(il);

            if (!cursor.TryGotoNext(
                    MoveType.After,
                    x => x.Match(OpCodes.Ldloc_2),
                    x => x.Match(OpCodes.Callvirt),
                    x => x.Match(OpCodes.Ldelem_Ref)))
            {
                cursor.Index = cursor.Instrs.Count;
            }
            else
            {
                cursor.Index--;
            }

            cursor.Emit(OpCodes.Ldc_I4_0);
            cursor.Emit(OpCodes.Ldc_I4_3);
            cursor.EmitCall(typeof(Program).GetMethod(nameof(Clamp), new[] { typeof(int), typeof(int), typeof(int) })!);

            if (!cursor.TryGotoNext(
                    MoveType.Before,
                    x => x.Match(OpCodes.Ldloc_1),
                    x => x.Match(OpCodes.Callvirt),
                    x => x.Match(OpCodes.Stloc_S)))
            {
                cursor.Index = cursor.Instrs.Count;
            }

            cursor.Emit(OpCodes.Ret);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int Clamp(int value, int min, int max)
        {
            if (min > max)
                throw new ArgumentException($"'{min}' cannot be greater than {max}.");
            if (value < min)
                return min;
            return value > max ? max : value;
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions