2017-10-25 32 views
5

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 ...

+1

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

+2

Niestety, nie mogę tego odtworzyć. Czy 'ldarg.0' jest' call' konieczne? – IllidanS4

+2

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. –

Odpowiedz

2

Niestety, wydaje się, uderzę błąd CLR ...

Wszystko działa podczas korzystania z kompilatora JIT starszych:

set COMPLUS_useLegacyJit=1

I nie był w stanie wyizolować specyficzne ustawienie RyuJit co może być przyczyną tego. Postępowałem zgodnie z zaleceniami w tym artykule:
https://github.com/Microsoft/dotnet/blob/master/Documentation/testing-with-ryujit.md

Dziękuję wszystkim, którzy pomogli!

+2

Możesz chcieć otworzyć problem dla repozytorium CoreCLR (które zawiera RyuJIT); istnieje kilka (https://github.com/dotnet/coreclr/search?type=Issues) dla 'localloc' już.'localloc' nie jest dozwolone w' finally', więc możliwe jest, że jitter się myli i całkowicie go tu zaprzepaści, nawet jeśli nie znajduje się w bloku. –

+0

@JeroenMostert dzięki! Podejrzewałem, że tak być może ... Korzystam z .NET 4.6, więc sprawdzę, czy to się powiela na najnowszym CoreCLR. Zauważyłem podobny błąd, który został już naprawiony, więc prawdopodobnie jest już naprawiony: https://github.com/dotnet/coreclr/blob/master/tests/src/JIT/Regression/VS- ia64-JIT/V1.2-M01/b10841/repro_good.il – valiano

+0

Przyjemny haczyk! Zastanawiam się, jaka jest przyczyna błędu. – IllidanS4