I have the following part of the reduced CIL code.
When this CIL method is executed, the CLR throws an InvalidProgramException:
.method assembly hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Collections.Generic.IEnumerable`1<class System.Windows.Input.StylusDeviceBase> styluses) cil managed { .locals init (class [mscorlib]System.Collections.Generic.IEnumerator`1<class System.Windows.Input.StylusDeviceBase> V_0, class System.Windows.Input.StylusDeviceBase V_1) ldc.i4.8 // These instructions cause CIL to break conv.u // localloc // pop // ldarg.0 newobj instance void class [mscorlib]System.Collections.Generic.List`1<class System.Windows.Input.StylusDevice>::.ctor() call instance void class [mscorlib]System.Collections.ObjectModel.ReadOnlyCollection`1<class System.Windows.Input.StylusDevice>::.ctor(class [mscorlib]System.Collections.Generic.IList`1<!0>) ldarg.1 callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<class System.Windows.Input.StylusDeviceBase>::GetEnumerator() stloc.0 .try { leave.s IL_0040 } finally { endfinally } IL_0040: ret } // end of method StylusDeviceCollection::.ctor
My question is: why is this CIL code invalid?
A few tombs:
- If localloc removed, the code works fine. As far as I know, localloc replaces the size of the parameter in the stack with an address, so the stack remains balanced, AFAICT.
- If the try and finally blocks are removed, the code works fine.
- If the first block of instructions containing localloc is moved after the try-finally block, the code works fine.
So this looks like something in a combination of localloc and try-finally.
A bit of background:
I got to this point after an InvalidProgramException was thrown for the original method due to some tools made at runtime. My approach for debugging this, to find out what is wrong with the device, is as follows:
- Parsing a failed DLL using
ildasm - Applying Toolkit Code to a Failure Method
- Recreating a DLL from a Modified IL with
ilasm - Starting the program again, and checking its failure
- Continue to reduce the IL code of the failure method, gradually, up to the minimum scenario that causes the problem (and tries not to introduce errors along the way).
Unfortunately, peverify.exe /IL did not indicate any errors. I tried to console the ECMA specification and Serge Lidin Expert.NET IL, but I could not understand what was going wrong.
Is there anything basic that I am missing?
Edit:
I updated the corresponding IL code a bit to make it more complete (without changing instructions). The second block of instructions, including ldarg , newobj , etc., is taken as from the working code - the source code of the method.
Which is strange for me, deleting either localloc or .try - finally , the code works, but none of them, as far as I know, should change the stack balancing compared to the fact that re is present in the code.
Here, the IL code was decompiled in C # using ILSpy:
internal unsafe StylusDeviceCollection(IEnumerable<StylusDeviceBase> styluses) { IntPtr arg_04_0 = stackalloc byte[(UIntPtr)8]; base..ctor(new List<StylusDevice>()); IEnumerator<StylusDeviceBase> enumerator = styluses.GetEnumerator(); try { } finally { } }
Edit 2:
Other observations:
- Taking the localloc block of the localloc code and moving it to the end of the function, the code works fine - so it seems that the code itself is in order.
- The problem does not reproduce when I paste a similar IL code into the global test function hello.
I am very puzzled ...
I would like more information from an InvalidProgramException. The CLR does not seem to bind the exact cause of the failure to the exception object. I also thought about debugging using the CoreCLR debug build, but fortunately the program I am debugging is not compatible with it ...