MSIL: простой пользовательский код говорит о неверном коде IL - System.InvalidProgramException

Я пытаюсь узнать больше о.NET MSIL, и поэтому я хотел написать свою собственную сборку IL. Я застрял на следующем примере.

Сначала я работаю на OSX, используя Mono "Mono JIT-компилятор версии 4.8.1 (mono-4.8.0-branch/22a39d7 Fri Apr 7 12:00:08 EDT 2017)", и моя сборка il выглядит следующим образом. Полный файл:

.assembly extern mscorlib
{
  .ver 4:0:0:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'Application'
{
  .hash algorithm 0x00008004
  .ver  0:0:0:0
}
.module sample.exe // GUID = {B239DF75-1D4B-4A83-B8B6-99BDEFD7B8A6}


  .class public auto ansi MainType
    extends [mscorlib]System.Object
  {

    // method line 1
    .method public specialname rtspecialname
           instance default void '.ctor' ()  cil managed
    {
        // Method begins at RVA 0x20ec
    // Code size 7 (0x7)
    .maxstack 8
              ldarg.0
              call instance void object::'.ctor'()
              ret
    } // end of method MainType::.ctor

    // method line 2
    .method public static hidebysig
           default void Main (string[] A_0)  cil managed
    {
        // Method begins at RVA 0x20f4
        .entrypoint
        // Code size 87 (0x57)
        .maxstack 6
        .locals init (
          int32[]   V_0,
          int32 V_1)
                  // Allocating array of size 200
                  ldc.i4 200
                  newarr int32[]
                  stloc.0

                  // Pushing the numbers I want to output
                  ldc.i4 100
                  ldc.i4 101
                  ldc.i4 102

                  // 3 is the total number of previous pushed numbers
                  // it functions in this programm as a counter which
                  // will be decremented
                  ldc.i4 3
                  stloc.1      // storing the 3 in the temp variable V_1

                  ldloc.0      // push the array reference onto stack 
                  ldc.i4 0     // push the index onto the stack 
                  ldloc.1      // loading the 3 from the temp int32 var (V_1), push it onto the stack 
                  stelem.i4    // save the value into the array at index 0 (defined above aka pushed on the stack)

                  // when it enters first, on the top of the stack should
                  // be the value 102 which we want to print out:
           loop:  call void class [mscorlib]System.Console::WriteLine(int32) // the call should consume 102 from the stack and print it out

                  // loading the counter from array
                  ldloc.0
                  ldc.i4 0
                  ldelem.i4

                  ldc.i4 1   // pushing 1
                  sub        // subtract the counter by 1

                  // Storing the result which is on the stack
                  // in the array index 0 (our counter):
                  stloc.1
                  ldloc.0
                  ldc.i4 0
                  ldloc.1
                  stelem.i4

                  // Loading the counter value again onto stack to perform
                  // the check for branching:
                  ldloc.0
                  ldc.i4 0
                  ldelem.i4

                  ldc.i4 0 // pushing 0 to see if the counter is 0
                  ceq
                  brfalse loop // if its not yet 0 then we jump to the loop label; which means it should on the next call to WriteLine consume the next value which is on the stack, which should be 101

                  ret
    } // end of method MainType::Main

  } // end of class MainType

Интересная или "проблемная" часть находится здесь:

    .method public static hidebysig
           default void Main (string[] A_0)  cil managed
    {
        // Method begins at RVA 0x20f4
        .entrypoint
        // Code size 87 (0x57)
        .maxstack 6
        .locals init (
          int32[]   V_0,
          int32 V_1)
                  // Allocating array of size 200
                  ldc.i4 200
                  newarr int32[]
                  stloc.0

                  // Pushing the numbers I want to output
                  ldc.i4 100
                  ldc.i4 101
                  ldc.i4 102

                  // 3 is the total number of previous pushed numbers
                  // it functions in this programm as a counter which
                  // will be decremented
                  ldc.i4 3
                  stloc.1      // storing the 3 in the temp variable V_1

                  ldloc.0      // push the array reference onto stack 
                  ldc.i4 0     // push the index onto the stack 
                  ldloc.1      // loading the 3 from the temp int32 var (V_1), push it onto the stack 
                  stelem.i4    // save the value into the array at index 0 (defined above aka pushed on the stack)

                  // when it enters first, on the top of the stack should
                  // be the value 102 which we want to print out:
           loop:  call void class [mscorlib]System.Console::WriteLine(int32) // the call should consume 102 from the stack and print it out

                  // loading the counter from array
                  ldloc.0
                  ldc.i4 0
                  ldelem.i4

                  ldc.i4 1   // pushing 1
                  sub        // subtract the counter by 1

                  // Storing the result which is on the stack
                  // in the array index 0 (our counter):
                  stloc.1
                  ldloc.0
                  ldc.i4 0
                  ldloc.1
                  stelem.i4

                  // Loading the counter value again onto stack to perform
                  // the check for branching:
                  ldloc.0
                  ldc.i4 0
                  ldelem.i4

                  ldc.i4 0 // pushing 0 to see if the counter is 0
                  ceq
                  brfalse loop // if its not yet 0 then we jump to the loop label; which means it should on the next call to WriteLine consume the next value which is on the stack, which should be 101

                  ret
    } // end of method MainType::Main

Я получаю следующую ошибку:

Unhandled Exception:
System.InvalidProgramException: Invalid IL code in MainType:Main         (string[]): IL_0056: ret       


[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException:     Invalid IL code in MainType:Main (string[]): IL_0056: ret       

И "IL_0056: ret" указывает на строку (когда я разбираю получившийся исполняемый файл):

IL_0044:  ldc.i4 0
IL_0049:  ldelem.i4 
IL_004a:  ldc.i4 0
IL_004f:  ceq 
IL_0051:  brfalse IL_0028

IL_0056:  ret   //// <<<< HERE
} // end of method MainType::Main

Некоторые важные примечания: * При сохранении и присвоении массива кажется, что я всегда делаю ненужные "танцы". Я делаю это, потому что в конце я хочу создать машину стека, которая работает только со значениями из стека. Это также причина, почему код выглядит следующим образом. Прежде чем написать это, я хотел посмотреть, как я могу сделать это в MSIL.

Буду очень признателен, если кто-нибудь скажет мне, что здесь не так. Большое спасибо.

Это мой первый вопрос, и я надеюсь, что предоставил всю необходимую информацию, чтобы помочь мне:-)

PS: Я также пытался использовать MonoDevelop с "Отладочным приложением", но я не мог извлечь из него никаких значимых данных, также gdb не работал для меня хорошо.

PPS: Я думал, что у меня все еще есть значения в стеке, но когда я добавляю pop в конце (или больше), он просто говорит мне, что pop является недействительным.

РЕДАКТИРОВАТЬ:

Я попытался напечатать какой-то вывод, чтобы увидеть, что происходит не так, и выяснил, что между меткой 'loop' он неправильно выводит значение из стека. Я еще не мог понять, почему, но вот пример вывода, который показывает это ясно:

102
LOOP START
101
---
101
---
101
---
exit now
100

Даже если я изменю код il дальше, он печатает больше значений:

LOOP START
102
101
100
---
102
101
100
---
102
101
100
---
exit now

это не выскакивает из стека. По каким-то странным причинам, выполнение 'CallLine' не приводит к извлечению значения или, как кажется, делает резервное копирование и восстановление стека, но это только мое предположение. Имеет это для работы с: .NET CIL манипуляциями со стеком оценки ("Ограничения обратной ветви", ссылка: http://jilc.sourceforge.net/ecma_p3_cil.shtml)

0 ответов

Другие вопросы по тегам