Mam następujący fragment zmniejszonego kodu CIL.
Kiedy ta metoda CIL jest wykonywany, InvalidProgramException jest wyrzucane przez CLR:Dlaczego localloc łamie tę metodę CIL?
.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
Moje pytanie brzmi, dlaczego ten kod CIL nieważny?
Kilka oberwań:
- Jeśli zostanie usunięty localloc
, kod działa poprawnie. Według mojej wiedzy, localloc
zastępuje rozmiar parametru na stosie za pomocą adresu, więc stos pozostaje zrównoważony, AFAICT.
- Jeśli bloki try i finally zostaną usunięte, kod działa poprawnie.
- Jeśli pierwszy blok instrukcji zawierający localloc
jest przenoszony do po bloku try-finally, kod działa poprawnie.
Wygląda na to, że coś w połączeniu z localloc i try-finally.
Niektóre tła:
mam do tego punktu po InvalidProgramException został wrzucony do oryginalnej metody, ze względu na pewne instrumentarium wykonane w czasie wykonywania. Moje podejście do debugowania to, aby dowiedzieć się, co jest nie tak z oprzyrządowaniem, wynosi:
- Demontaż uszkodzonego DLL z
ildasm
- Stosując kod oprzyrządowania do sposobu upaść
- odtworzenie DLL ze zmodyfikowanego IL z
ilasm
- Uruchomienie programu ponownie i weryfikacji wywala
- utrzymywać redukujący kod IL metody upaść gradualy, aż do minimalnej scenariusza, który powoduje problem (i nie próbuje int roduce robaki po drodze ...)
Niestety, peverify.exe /IL
nie wskazał żadnego błędu. Próbowałem pocieszyć specyfikację ECMA i książkę eksperta Serge'a Lidina dla platformy .NET IL, ale nie mogłem ustalić, co jest nie tak.
Czy brakuje czegoś podstawowego?
Edit:
ja lekko uaktualniony kod IL w pytaniu, aby uczynić go bardziej kompletne (bez modyfikowania instrukcje). Drugi blok instrukcji, w tym ldarg
, newobj
itd., Jest pobierany z działającego kodu - oryginalnego kodu metody.
Co dziwne dla mnie jest to, usuwając albo localloc
lub .try
- finally
, kod działa - ale żaden z nich, o ile mi wiadomo, należy zmienić równoważenie stosie, w porównaniu jeśli są one obecne w kodzie .
Oto kod IL dekompilowana w C# z 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:
Więcej obserwacje:
- Biorąc blok localloc
kodu IL, i przeniesienie go do końca funkcji, kod działa dobrze - więc wydaje się, że kod sam w sobie jest OK.
- Problem nie powtarza się po wklejeniu podobnego kodu IL w funkcji testu Witaj świecie.
Jestem bardzo zaskoczony ...
Szkoda, że nie było sposobu, aby uzyskać więcej informacji z InvalidProgramException. Wydaje się, że CLR nie dołącza dokładnej przyczyny niepowodzenia do obiektu wyjątku. Myślałem także o debugowania CoreCLR debugowania, ale unforunately program mam debugowania nie jest kompatybilny z nim ...
Kiedy otwierasz skompilowaną bibliotekę DLL w programie ILSpy, pokazuje ona kod, który chcesz zobaczyć? (Prawdopodobnie tylko w widoku IL - to prawdopodobnie nie będzie dekompilować do C#, chociaż może się mylę.) – xxbbcc
Niestety, nie mogę tego odtworzyć. Czy 'ldarg.0' jest' call' konieczne? – IllidanS4
Dość niejasne, w jaki sposób można użyć pliku peverify.exe, który nie lubi localloc. To polecenie ldarg.0 nie wyważa stosu, wywołujesz domyślny konstruktor listy <>. Po prostu go skasuj. –