MSIL Tutorial

 
 
 

MSIL Tutorial

Introduction

Microsoft Intermediate Language (MSIL) is a language used as the output of a number of compilers (C#, VB, .NET, and so forth). The ILDasm (Intermediate Language Disassembler) program that ships with the .NET Framework SDK (FrameworkSDK\Bin\ildasm.exe) allows the user to see MSIL code in human-readable format. By using this utility, we can open any .NET executable file (EXE or DLL) and see MSIL code.

The ILAsm program (Intermediate Language Assembler) generates an executable file from the MSIL language. We can find this program in the WINNT\Microsoft.NET\Framework\vn.nn.nn directory.

Any Visual C++ programmer starting with .NET development is interested in what happens in the low level of the .NET Framework. Learning MSIL gives a user the chance to understand some things that are hidden from a programmer working with C# or VB.NET. Knowing MSIL gives more power to a .NET programmer. We never need to write programs in MSIL directly, but in some difficult cases it is very useful to open the MSIL code in ILDasm and see how things are done.

A MSIL reference in DOC format is available to a .NET developer and may be found in the Framework SDK directory:

  • FrameworkSDK\Tool Developers Guide\docs\Partition II Metadata.doc (Metadata Definition and Semantics). In this file, I found a description of all MSIL directives such as .entrypoint.locals, and so on.
  • FrameworkSDK\Tool Developers Guide\docs\Partition III CIL.doc (CIL Instruction Set) contains a full list of the MSIL commands.

I also used in my work in an ILDAsm tutorial from MSDN and an excellent article in the May 2001 issue of MSDN Magazine: "ILDASM is Your New Best Friend" by John Robbins.

I think the best way to learn the language is to write some programs in it. This is a reason I decided to make several small MSIL programs. Actually, I didn't write this code—the C# compiler generated it. I made some minor changes and added a lot of notes describing how MSIL is working.

Reading the sample projects attached to this article may help a .NET programmer understand Intermediate Language and easily read MSIL code when this is necessary.

General Information

All operations in MSIL are executed on the stack. When a function is called, its parameters and local variables are allocated on the stack. Function code starting from this stack state may push some values onto the stack, make operations with these values, and pop values from the stack.

Execution of both MSIL commands and functions is done in three steps:

  1. Push command operands or function parameters onto the stack.
  2. Execute the MSIL command or call function. The command or function pops their operands (parameters) from the stack and pushes onto the stack result (return value).
  3. Read result from the stack.

Steps 1 and 3 are optional. For example, the void function doesn't push a return value to the stack.

The stack contains objects of value types and references to objects of reference type. Reference type objects are kept in the heap.

MSIL commands used to push values onto the stack are called ld... (load). Commands used to pop values from the stack are called st... (store), because values are stored in variables. Therefore, we will call the pushoperation loading and the pop operation storing.

Sample Projects

The code attached to this article contains a number of Console Applications written in MSIL. To build them, ensure that the ILAsm program is available through the PATH. Each project is done as a Visual Studio solution. The source IL file may be opened in the VS Text Editor. The build command runs the ILAsm program, which generates an exe file in the project directory. The run command executes this file. At the end of each program, I added these lines, which can be written in C#:

  1. Console.WriteLine("Press Enter to continue");
  2. Console::Read();

This is done to see the program output when it is run from Windows Explorer.

Here's a list of the included projects:

  1. PrintString—prints the string to the console.
  2. XequalN—assigns a value to the int variable and prints it to the console.
  3. Operations—reads two numbers from the console; makes operations +, -, and *; and shows the result.
  4. Array1—allocates int array, assign values to its elements; print elements and array length.
  5. Compare—enters two numbers and prints the minimum.
  6. Array2—fills array elements in loop and prints some elements.
  7. Unsafe—uses unsafe pointers to access array elements.
  8. PInvoke—calls Win32 API.
  9. Classes—works with classes.
  10. Exception—handles exceptions.

I suggest that you read these projects in the same order as they are described here. In the projects' descriptions given below, I explain each new MSIL command used in the program and show some code fragments.

PrintString Program

PrintString is the MSIL Hello, World application.

MSIL directives used in the code are as follows:

  • .entrypoint—defines the application entry point (the function called by .NET Runtime when the program starts).
  • .maxstack—defines the maximum stack depth used by the function code. The C# compiler sets always the exact value for each function. In the sample project, I set this value to 8.

MSIL commands are as follows:

  • ldstr string—loads the string constant onto the stack.
  • call function(parameters)—calls the static function. Parameters for the function should be loaded onto the stack before this call.
  • pop—pops a value from the stack. Used when we don't need to store a value in the variable.
  • ret—returns from a function.

Calling the static function is simple. We push to stack the function parameters, call the function, and read from the stack function return value (if function is not void). Console.WriteLine is an example of such a function.

Here is the code:

  1. .assembly PrintString{}
  2.  
  3. /*
  4. Console.WriteLine("Hello, World)"
  5. */
  6.  
  7. .method staticpublicvoid main() il managed
  8. {
  9. .entrypoint // this function is the application
  10. // entry point
  11.  
  12. .maxstack 8
  13.  
  14.  
  15. // *****************************************************
  16. // Console.WriteLine("Hello, World)";
  17. // *****************************************************
  18. ldstr "Hello, World"// load string onto stack
  19.  
  20. // Call static System.Console.Writeline function
  21. // (function pops string from the stack)
  22. call void[mscorlib]System.Console::WriteLine
  23. (classSystem.String)
  24.  
  25.  
  26. // *****************************************************
  27. ldstr "Press Enter to continue"
  28. call void[mscorlib]System.Console::WriteLine
  29. (classSystem.String)
  30.  
  31.  
  32. // Call the System.Console.Read function
  33. call int32 [mscorlib]System.Console::Read()
  34.  
  35. // The pop instruction removes the top element from the stack.
  36. // (remove number returned by Read() function)
  37. pop
  38. // *****************************************************
  39.  
  40. ret
  41. }

XequalN Program

The program assigns a value to the integer variable and prints it to the console window.

Commands:

  • ldc.i4.n—loads a 32-bit constant (n from 0 to 8) onto the stack
  • stloc.n—stores a value from the stack to local variable number n (n from 0 to 3)

Code:

  1. .assembly XequalN{}
  2.  
  3. // int x;
  4. // x = 7;
  5. // Console.WriteLine(x);
  6.  
  7. .method staticpublicvoid main() il managed
  8. {
  9. .entrypoint
  10.  
  11. .maxstack 8
  12.  
  13. .locals init ([0] int32 x)// Allocate local variable
  14. // *****************************************************
  15. // x = 7;
  16. // *****************************************************
  17. ldc.i4.7// load constant onto stack
  18. stloc.0// store value from stack to
  19. // var. 0
  20. // *****************************************************
  21. // Console.WriteLine(x);
  22. // *****************************************************
  23. ldloc.0// load var.0 onto stack
  24. call void[mscorlib]System.Console::WriteLine(int32)
  25.  
  26. ret
  27. }
  28.  

Operations Program

The program reads two numbers from the console, makes simple math operations with them, and shows the result.

Commands:

  • add—adds two values. Command parameters should be loaded onto the stack before the call. The function pops the parameters and pushes a result onto the stack.
  • sub—subtracts two values.
  • mul—multiplies two values.

Code fragments:

  1. .assembly Operations{}
  2. /*
  3. // This program works as C# code:
  4. int x, y, z;
  5. string s;
  6. Console.WriteLine("Enter x:");
  7. s = Console.ReadLine();
  8. x = Int32.Parse(s);
  9. Console.WriteLine("Enter y:");
  10. s = Console.ReadLine();
  11. y = Int32.Parse(s);
  12. z = x + y;
  13. Console.Write("x + y = ");
  14. Console.Write(z);
  15. Console.WriteLine("");
  16. z = x - y;
  17. Console.Write("x - y = ");
  18. Console.Write(z);
  19. Console.WriteLine("");
  20. z = x * y;
  21. Console.Write("x * y = ");
  22. Console.Write(z);
  23. Console.WriteLine("");
  24. */
  25.  
  26. .method staticpublicvoid main() il managed
  27. {
  28. .entrypoint
  29. .maxstack 8
  30.  
  31. .locals init ([0] int32 x,
  32. [1] int32 y,
  33. [2] int32 z,
  34. [3]string s)
  35.  
  36. // *****************************************************
  37. // Console.WriteLine("Enter x:");
  38. // *****************************************************
  39. ldstr "Enter x:"// load string onto stack
  40. call void[mscorlib]System.Console::WriteLine(string)
  41.  
  42. // *****************************************************
  43. // s = Console.ReadLine();
  44. // *****************************************************
  45. call string[mscorlib]System.Console::ReadLine()
  46. stloc.3// store value to var. 3
  47. // *****************************************************
  48. // x = Int32.Parse(s);
  49. // *****************************************************
  50. ldloc.3// load variable 3 onto stack
  51. // Call System.Int32::Parse(string)
  52. // Function pops string from stack and pushes to stack
  53. // int32 value - result of parsing.
  54. call int32 [mscorlib]System.Int32::Parse(string)
  55.  
  56. stloc.0// store value to var. 0
  57. // *****************************************************
  58. // Same operations with variable y
  59. // *****************************************************
  60. ldstr "Enter y:"
  61. // load string
  62. call void[mscorlib]System.Console::WriteLine(string)
  63. // call
  64. call string[mscorlib]System.Console::ReadLine()
  65. // call
  66. stloc.3
  67. // store to var. 3
  68. ldloc.3
  69. // load var. 3
  70. call int32 [mscorlib]System.Int32::Parse(string)
  71. // call
  72. stloc.1
  73. // store to var. 1
  74.  
  75. // *****************************************************
  76. // z = x + y;
  77. // *****************************************************
  78. ldloc.0// load variable 0 onto stack
  79. ldloc.1// load variable 1 onto stack
  80.  
  81. // pop two values from the stack, add them and push result
  82. // onto stack
  83. add
  84.  
  85. stloc.2// store to variable 2
  86. // *****************************************************
  87. // Console.Write("x + y = ");
  88. // *****************************************************
  89. ldstr "x + y = "// load string onto stack
  90. call void[mscorlib]System.Console::Write(string)
  91.  
  92. // *****************************************************
  93. // Console.Write(z);
  94. // *****************************************************
  95. ldloc.2// load variable 2 onto stack
  96. call void[mscorlib]System.Console::Write(int32)
  97.  
  98. // *****************************************************
  99. // Console.WriteLine("");
  100. // *****************************************************
  101. ldstr ""// load string onto stack
  102. call void[mscorlib]System.Console::WriteLine(string)
  103.  
  104. // Same operations with subtraction and multiplication ...
  105.  
  106. ret
  107. }

Array1 Program

The program allocates the int array, assigns values to its elements, and then prints the elements and array length.

Commands:

  • newarr type—creates an array of type elements. The array size should be loaded onto the stack before a call to this command. Loads onto the stack a reference to the array.
  • stelem.i4—assigns a value to an array member. The value has type Int32. The array reference, index, and value should be loaded onto the stack before a call to this command.
  • ldelema type—loads to the stack the address of an array element. The array reference and index should be loaded onto the stack before a call to this command. The address is used to call a non-static class function (see later).
  • ldlen—loads the length of an array onto the stack. The array reference should be loaded onto the stack before a call to this command.
  • ldloca.s variable—loads the address of the variable onto the stack.
  • ldc.i4.s value—loads an Int32 constant onto the stack (used for values more than 8).
  • conv.i4—converts value from the stack to Int32.
  • call instance function(arguments)—calls a non-static class function. Before a call to a non-static function, we need to load onto the stack the address of the class object (used first as a hidden parameter, as in C++) and function arguments. In this sample object, the address is loaded using theldelema and ldloca commands.

In some code fragments in this sample, I wrote in the notes to stack the state starting after the last local variable. In this sample, we see the variable generated by the compiler. This variable is used to make the call to the non-static class function.

Code:

  1. .assembly Array1{}
  2.  
  3. /*
  4. // This program works as C# code:
  5. int[] x = new int[5];
  6. x[0] = 10;
  7. x[1] = 20;
  8. Console.WriteLine("x[0] = " + x[0].ToString());
  9. Console.WriteLine("x[1] = " + x[1].ToString());
  10. Console.WriteLine("Array length = " + x.Length.ToString());
  11. */
  12.  
  13. .method staticpublicvoid main() il managed
  14. {
  15. .entrypoint
  16. .maxstack 8
  17.  
  18. .locals init ([0] int32[] x,
  19. [1] int32 tmp)// generated by compiler
  20. // *****************************************************
  21. // x = new int[5];
  22. // *****************************************************
  23. ldc.i4.5// load constant onto stack
  24.  
  25. // create array and store reference onto stack
  26. newarr [mscorlib]System.Int32
  27.  
  28. // Store (pop) value from the stack and place it to local
  29. // variable 0.
  30. stloc.0
  31.  
  32. // *****************************************************
  33. // x[0] = 10;
  34. // *****************************************************
  35. ldloc.0// Load local variable 0 onto stack (array)
  36. ldc.i4.0// Load constant 0 to the stack (index)
  37. ldc.i4.s 10// Load constant 10 to the stack (value)
  38. stelem.i4 // array[index] = value
  39. // The same operations with element number 1...
  40. // ***************************************************
  41. // Console.WriteLine("x[0] = " + x[0].ToString());
  42. // ***************************************************
  43. ldstr "x[0] = "// load string onto stack
  44. // STACK: "x[0] = " (stack is shown from local
  45. // variables)
  46. ldloc.0// load variable 0 onto stack
  47. ldc.i4.0// load constant 0 onto stack
  48. // STACK: "x[0] = " -> x -> 0
  49. // Load address of array element onto stack.
  50. ldelema [mscorlib]System.Int32
  51. // STACK: "x[0] = " -> pointer to Int32 instance
  52. // 10
  53. // Call non-static function System.Int32::ToString().
  54. call instance string[mscorlib]System.Int32::ToString()
  55. // STACK: "x[0] = " -> "10"
  56. // call static System.String::Concat(string, string)
  57. call string[mscorlib]System.String::Concat
  58. (string,string)
  59. // STACK: "x[0] = 10"
  60. // call static System.Console::WriteLine(string)
  61. call void[mscorlib]System.Console::WriteLine(string)
  62. // STACK: empty
  63. // The same operations with element number 1 ...
  64. // *****************************************************
  65. // Console.WriteLine("Array length = " + x.Length.ToString());
  66. // *****************************************************
  67. ldstr "Array length = "
  68. // load string onto stack
  69. // STACK: "Array length = "
  70. ldloc.0
  71. // load variable 0 to stack
  72. // STACK: "Array length = " -> x
  73. ldlen
  74. // push the length of array onto stack
  75. // STACK: "Array length = " -> 5
  76. conv.i4
  77. // Convert to int32, pushing int32 onto stack
  78. // STACK: "Array length = " -> 5
  79. stloc.1
  80. // store to local variable 1 (tmp)
  81. // STACK: "Array length = "
  82. ldloca.s tmp
  83. // load address of variable tmp onto stack
  84. // STACK: "Array length = " -> &tmp
  85. call instance string[mscorlib]System.Int32::ToString()
  86. // STACK: "Array length = " -> "5"
  87. call string[mscorlib]System.String::Concat
  88. (string,string)
  89. // STACK: "Array length = 5"
  90. call void[mscorlib]System.Console::WriteLine(string)
  91. // STACK: empty
  92.  
  93. ret
  94. }

Compare Program

The program reads two numbers and prints their minimum.

Commands:

  • bge.s label—goes to label if value1 is greater than or equal to value 2. Values 1 and 2 should be loaded onto the stack before a call to this command.
  • br.s label—goes to label.
  • box value type—converts a value type to an Object and loads the Object's reference onto the stack.

Boxing in this program is caused by the C# line: Console.WriteLine("{0:d}", z);
Writing this line in this way: Console.WriteLine(z.ToString());
doesn't cause boxing.

Code:

  1. .assembly Compare{}
  2. /*
  3. int x, y, z;
  4. string s;
  5. Console.WriteLine("Enter x:");
  6. s = Console.ReadLine();
  7. x = Int32.Parse(s);
  8. Console.WriteLine("Enter y:");
  9. s = Console.ReadLine();
  10. y = Int32.Parse(s);
  11. if ( x < y )
  12. z = x;
  13. else
  14. z = y;
  15. Console.WriteLine("{0:d}", z);
  16. */
  17.  
  18. .method staticpublicvoid main() il managed
  19. {
  20. .entrypoint
  21. .maxstack 8
  22. .locals init ([0] int32 x,
  23. [1] int32 y,
  24. [2] int32 z,
  25. [3]string s)
  26.  
  27. // *****************************************************
  28. // Console.WriteLine("Enter x:");
  29. // *****************************************************
  30. ldstr "Enter x:"// load string onto stack
  31. call void[mscorlib]System.Console::WriteLine(string)
  32. // *****************************************************
  33. // s = Console.ReadLine();
  34. // *****************************************************
  35. call string[mscorlib]System.Console::ReadLine()
  36. stloc.3// store to var. 3
  37. // *****************************************************
  38. // x = Int32.Parse(s);
  39. // *****************************************************
  40. ldloc.3// load var. 3 onto stack
  41. call int32 [mscorlib]System.Int32::Parse(string)
  42. stloc.0// store to var. 0
  43. // The same operations for y ...
  44. // *****************************************************
  45. // branch
  46. // if ( x >= y ) goto L_GR;
  47. // *****************************************************
  48. ldloc.0// load x onto stack (value 1)
  49. ldloc.1// load y onto stack (value 2)
  50. bge.s L_GR // goto L_GR if value1 is greater
  51. // than or equal to value2
  52. // *****************************************************
  53. // z = x
  54. // *****************************************************
  55. ldloc.0// load variable 0 onto stack
  56. stloc.2// store to variable 2
  57.  
  58. br.s L_CONTINUE // goto L_CONTINUE
  59.  
  60. L_GR:
  61.  
  62. // *****************************************************
  63. // z = y
  64. // *****************************************************
  65. ldloc.1// load variable 1 onto stack
  66. stloc.2// store to variable 2
  67.  
  68. L_CONTINUE:
  69.  
  70. // *****************************************************
  71. // Console.WriteLine("{0:d}", z);
  72. // NOTE: this line causes boxing.
  73. // *****************************************************
  74. ldstr "{0:d}"// load string onto stack
  75. ldloc.2// load variable 2 to stack (z)
  76. box [mscorlib]System.Int32// convert Int32 to Object
  77. call void[mscorlib]System.Console::WriteLine(string,object)
  78.  
  79. ret
  80. }

Array2 Program

The program fills an array in the loop and prints its elements. This time, we add the static functionShowNumber(int), which is called from main.

Commands:

  • blt.s label—goes to label if value 1 is less than value 2. Values 1 and 2 should be loaded onto the stack before a call to this command.
  • ldelem.i4—loads an array element onto the stack. A reference to the array and index should be loaded onto the stack before a call to this command.
  • ldarga.s argument—loads the address of the function argument onto the stack.

We can see in this program that the for loop is implemented in MSIL using labels.

Code:

  1. .assembly Array2{}
  2. /*
  3. int[] px = new int[100];
  4. int i;
  5. for ( i = 1; i < 100; i++ )
  6. {
  7. px[i] = i + 1;
  8. }
  9. ShowNumber(px[5]);
  10. ShowNumber(px[10]);
  11. static void ShowNumber(int n)
  12. {
  13. Console.WriteLine(n.ToString());
  14. }
  15. */
  16.  
  17. .method staticpublicvoid main() il managed
  18. {
  19. .entrypoint
  20. .maxstack 8
  21.  
  22. .locals init ([0] int32[] px,
  23. [1] int32 i)
  24.  
  25. // *****************************************************
  26. // x = new int[100]
  27. // *****************************************************
  28. ldc.i4.s 100// load constant onto
  29. // stack
  30. newarr [mscorlib]System.Int32// allocate Int32
  31. stloc.0// store to variable 0
  32. // *****************************************************
  33. // i = 1
  34. // *****************************************************
  35. ldc.i4.1// load constant onto stack
  36. stloc.1// store to variable 1
  37.  
  38. br.s CHECK_COUNTER // goto CHECK_COUNTER
  39.  
  40. START_LOOP:
  41. // *****************************************************
  42. // px[i] = i + 1;
  43. // *****************************************************
  44. ldloc.0// load variable 0 to stack
  45. // STACK: px
  46. ldloc.1// load variable 1 to stack
  47. // STACK; px -> i
  48. ldloc.1// load variable 1 to stack
  49. // STACK: px -> i -> i
  50. ldc.i4.1// load constant to stack
  51. // STACK: px -> i -> i -> 1.
  52. add // add last two values
  53. // STACK: px -> i -> i+1
  54. // (array,index,value)
  55. stelem.i4 // store value to array element:
  56. // array[index] = value
  57. // STACK: empty
  58. // *****************************************************
  59. // i = i + 1
  60. // *****************************************************
  61. ldloc.1// load variable 1 onto stack
  62. ldc.i4.1// load constant onto stack
  63. add // add
  64. stloc.1// store to variable 1
  65.  
  66. CHECK_COUNTER:
  67. // *****************************************************
  68. // if i < 100 goto start f loop
  69. // *****************************************************
  70. ldloc.1// load variable 1 onto stack
  71. ldc.i4.s 100// load constant onto stack
  72. blt.s START_LOOP // if value1 < value2 go to
  73. // START_LOOP
  74. // *****************************************************
  75. // ShowNumber(px[5]
  76. // *****************************************************
  77. ldloc.0// load variable 0 onto stack
  78. // (array)
  79. ldc.i4.5// load constant onto stack
  80. // (index)
  81. ldelem.i4 // load array element to stack
  82. call voidShowNumber(int32)// call ShowNumber
  83. // *****************************************************
  84. // ShowNumber(px[10]
  85. // *****************************************************
  86. ldloc.0
  87. ldc.i4.s 10
  88. ldelem.i4
  89. call voidShowNumber(int32)
  90.  
  91. ret
  92. }
  93.  
  94. .method staticpublicvoidShowNumber(int32 n) il managed
  95. {
  96. .maxstack 1
  97. ldarga.s n // load to stack address of argument n
  98. call instance string[mscorlib]System.Int32::ToString()
  99. call void[mscorlib]System.Console::WriteLine(string)
  100. ret
  101. }

Unsafe Program

The program fills and prints the int array using an unsafe pointer.

In this program, we see the new, unsafe types: int32* and int32&. The pinned keyword, used with a local variable, prevents GC from moving the object pointed to by the variable.

Commands:

  • dup—duplicates the value on the stack.
  • stind.i4—stores the value by address. The address and value should be loaded onto the stack before a call to this command.

Code:

  1. .assembly Unsafe{}
  2. /*
  3. int[] nArray = new int[5];
  4. int i;
  5. int* pCurrent;
  6. fixed ( int* pArray = nArray )
  7. {
  8. pCurrent = pArray;
  9. for ( i = 0; i < 5; i++ )
  10. {
  11. *pCurrent++ = i + 1;
  12. }
  13. }
  14. for ( i = 0; i < 5; i++ )
  15. {
  16. Console.WriteLine(nArray[i].ToString());
  17. }
  18. */
  19.  
  20. .method staticpublicvoid main() il managed
  21. {
  22. .entrypoint
  23. .maxstack 8
  24.  
  25. .locals ([0] int32[] nArray,
  26. [1] int32 i,
  27. [2] int32* pCurrent,
  28. [3] int32& pinned pArray)// GC doesn't move
  29. // pointed object
  30. // *****************************************************
  31. // nArray = new int[5];
  32. // *****************************************************
  33. ldc.i4.5// load constant 5 onto
  34. // stack
  35. newarr [mscorlib]System.Int32// create array Int32[5]
  36. stloc.0// store value from stack
  37. // to local var. o
  38. // *****************************************************
  39. // pArray = nArray (pArray = &nArray[0])
  40. // *****************************************************
  41. ldloc.0
  42. // load variable 0 onto stack (array)
  43. ldc.i4.0
  44. // load constant 0 onto stack (index)
  45. ldelema [mscorlib]System.Int32
  46. // load address of array[index] to stack
  47. stloc.3
  48. // store value from stack to local var. 3
  49. // *****************************************************
  50. // pCurrent = pArray;
  51. // *****************************************************
  52. ldloc.3// load variable 3 onto stack
  53. conv.i // convert to native int
  54. stloc.2// store to variable 2
  55. // *****************************************************
  56. // i = 0
  57. // *****************************************************
  58. ldc.i4.0// load constant 0 onto stack
  59. stloc.1// store value to var. 1
  60. // *****************************************************
  61. // goto CHECK_COUNTER
  62. // *****************************************************
  63. br.s CHECK_COUNTER
  64.  
  65. START_LOOP:
  66.  
  67. // *****************************************************
  68. // *pCurrent++ = i + 1 [STACK]
  69. // *****************************************************
  70. // 1) keep old pCurrent value on the stack and increment
  71. // pCurrent
  72. ldloc.2
  73. // load variable 2 onto stack [pCurrent]
  74. dup
  75. // duplicate the top value of the stack
  76. // [pCurrent pCurrent]
  77. ldc.i4.4
  78. // load constant 4 onto stack [pCurrent pCurrent 4]
  79. add
  80. // add [pCurrent pCurrent + 4]
  81. stloc.2
  82. // store from stack to variable 2 [pCurrent]
  83. // 2) write (i+1) by old pCurrent value kept on the stack
  84. ldloc.1
  85. // load variable 1 onto stack [pCurrent i]
  86. ldc.i4.1
  87. // load constant 1 onto stack [pCurrent i 1]
  88. add // add [pCurrent i+1]
  89. // address value
  90. stind.i4
  91. // store value by address [empty]
  92. // *****************************************************
  93. // i = i + 1
  94. // *****************************************************
  95. ldloc.1// load variable 1 onto stack
  96. ldc.i4.1// load constant 1 onto stack
  97. add // add
  98. stloc.1// store to variable 1
  99.  
  100. CHECK_COUNTER:
  101.  
  102. // *****************************************************
  103. // if (i < 5) goto START_LOOP;
  104. // *****************************************************
  105. ldloc.1// load variable 1 onto stack
  106. ldc.i4.5// load constant 5 onto stack
  107. blt.s START_LOOP // goto if less
  108. // *****************************************************
  109. // pArray = 0 fixed block finished
  110. // *****************************************************
  111. ldc.i4.0// load constant 0 to stack
  112. conv.u // convert to native unsigned int,
  113. // pushing native int on stack
  114. stloc.3// store in variable 3
  115. // print array elements to console...
  116.  
  117. ret
  118. }

PInvoke Program

The program shows the computer name using the Win32 API GetComputerName and MessageBox. API declarations in MSIL look like this:

  1. .method public hidebysig static pinvokeimpl("kernel32.dll"
  2. autochar winapi)
  3. int32 GetComputerName(
  4. class[mscorlib]System.Text.StringBuilder
  5. marshal( lptstr) buffer,
  6. int32& size) cil managed preservesig
  7. {
  8. }
  9.  
  10. .method public hidebysig static pinvokeimpl("User32.dll"
  11. autochar winapi)
  12. int32 MessageBox(nativeint hWnd,
  13. string marshal( lptstr) lpText,
  14. string marshal( lptstr) lpCaption,
  15. int32 uType) cil managed preservesig
  16. {
  17. }

They are called by the same rules as other any functions.

Classes Program

In previous programs, we called the class functions from the static function main. In this program, we will see how to write classes. The program contains two classes: Class1, with function main; and SampleClass, created in main.

Directive:

  • .field—defines class member. Used with keywords public, private, static, and so forth.

Commands:

  • stsfld static field—replaces the value of the static field with the value from the the stack.
  • ldfld field—loads a non-static class field onto the stack. The address of the class instance should be loaded onto the stack before a call to this command.
  • ldarg.n—loads argument number n onto the stack. In a non-static class function, argument 0 is a hidden argument and points to the this instance.
  • newobj constructor—creates a new instance of a class using constructor. Constructor parameters should be loaded onto the stack before this call. A reference to the created instance is loaded onto the stack.
  • callvirt instance function—calls a late-bound method on an object.

Code:

  1. .assembly Classes{}
  2. /*
  3. class SampleClass
  4. {
  5. private int m_n;
  6. private string m_s;
  7. public static int nStatic = 10;
  8. public SampleClass(int n, string s)
  9. {
  10. m_n = n;
  11. m_s = s;
  12. }
  13. public int Number
  14. {
  15. get
  16. {
  17. return m_n;
  18. }
  19. }
  20. public string String
  21. {
  22. get
  23. {
  24. return m_s;
  25. }
  26. }
  27. };
  28. class Class1
  29. {
  30. [STAThread]
  31. static void Main(string[] args)
  32. {
  33. SampleClass o = new SampleClass(1, "Sample");
  34. Console.WriteLine(SampleClass.nStatic.ToString());
  35. Console.WriteLine(o.Number.ToString());
  36. Console.WriteLine(o.String);
  37. }
  38. }
  39. */
  40.  
  41.  
  42. .classprivateauto ansi beforefieldinit SampleClass
  43. extends[mscorlib]System.Object
  44. {
  45. .field private int32 m_n // private int m_n;
  46. .field privatestring m_s // private string m_s;
  47. .field publicstatic int32 nStatic // public static int
  48. // nStatic;
  49. // private static constructor generated by compiler
  50. // (generated to initialize static class member)
  51. .method private hidebysig specialname rtspecialname static
  52. void.cctor() cil managed
  53. {
  54. .maxstack 8
  55.  
  56. // *************************************************
  57. // nStatic = 10
  58. // *************************************************
  59. ldc.i4.s 10// load constant onto stack
  60. // The stsfld instruction replaces the value of a static
  61. // field with a value from the stack
  62. stsfld int32 SampleClass::nStatic
  63.  
  64. ret
  65. }
  66.  
  67. // constructor
  68. // public SampleClass(int n, string s)
  69. //
  70. .method public hidebysig specialname rtspecialname
  71. instance void.ctor(int32 n,string s) cil managed
  72. {
  73. .maxstack 8
  74.  
  75. // *************************************************
  76. // Call base class constructor
  77. // *************************************************
  78. ldarg.0// Load argument 0 onto stack (hidden
  79. // pointer to this)
  80. // call Object constructor
  81. call instance void[mscorlib]System.Object::.ctor()
  82.  
  83. // *************************************************
  84. // m_n = n
  85. // *************************************************
  86. ldarg.0// Load argument 0 onto stack
  87. // (hidden pointer to this)
  88. ldarg.1// load argument 1 onto stack (n)
  89. // store value n in field m_n in instance pointed
  90. // by this
  91. stfld int32 SampleClass::m_n
  92.  
  93. // *************************************************
  94. // m_s = s
  95. // *************************************************
  96. ldarg.0// Load argument 0 onto stack
  97. // (hidden pointer to this)
  98. ldarg.2// load argument 1 onto stack (s)
  99. // store value s in field m_s in instance pointed
  100. // by this
  101. stfld stringSampleClass::m_s
  102.  
  103. ret
  104. }
  105.  
  106. // Number property
  107. .property instance int32 Number()
  108. {
  109. // call get_Number
  110. .get instance int32 SampleClass::get_Number()
  111. }
  112.  
  113. .method public hidebysig specialname instance int32
  114. get_Number() cil managed
  115. {
  116. .maxstack 8
  117.  
  118. // variable generated by compiler
  119. .locals ([0] int32 tmp)
  120.  
  121. // *************************************************
  122. // return m_n;
  123. // *************************************************
  124. ldarg.0
  125. // load argument 0 (this pointer)
  126. ldfld int32 SampleClass::m_n
  127. // load field of object pointed by stack value
  128. stloc.0
  129. // store in variable 0
  130. ldloc.0
  131. // load variable 0 onto stack (return value
  132. // of function)
  133.  
  134. ret
  135. }
  136.  
  137. // String property
  138. .property instance stringString()
  139. {
  140. .get instance stringSampleClass::get_String()
  141. }
  142. .method public hidebysig specialname instance string
  143. get_String() cil managed
  144. {
  145. .maxstack 8
  146.  
  147. // variable generated by compiler
  148. .locals ([0]string tmp)
  149.  
  150. ldarg.0
  151. // load argument 0 (this pointer)
  152. ldfld stringSampleClass::m_s
  153. // load field of object pointed by stack value
  154. stloc.0
  155. // store in variable 0
  156. ldloc.0
  157. // load variable 0 onto stack (return value
  158. // of function)
  159. ret
  160. }
  161. }
  162.  
  163.  
  164. .classprivateauto ansi beforefieldinit Class1
  165. extends[mscorlib]System.Object
  166. {
  167. // public default constructor
  168. .method public hidebysig specialname rtspecialname
  169. instance void.ctor() cil managed
  170. {
  171. .maxstack 8
  172.  
  173. // *************************************************
  174. // Call base class constructor
  175. // *************************************************
  176. ldarg.0
  177. // load this pointer
  178. call instance void[mscorlib]System.Object::.ctor()
  179. // call Object constructor
  180.  
  181. ret
  182. }
  183.  
  184. // Main function
  185. .method private hidebysig staticvoidMain(string[] args)
  186. cil managed
  187. {
  188. // this method is the entry point to the application
  189. .entrypoint
  190.  
  191. // Custom attribute
  192. .custom instance void[mscorlib]System.
  193. STAThreadAttribute::.ctor()=(01000000)
  194.  
  195. .maxstack 8
  196.  
  197. .locals ([0]classSampleClass o,
  198. [1] int32 tmp)// generated by compiler
  199. // *************************************************
  200. // o = new SampleClass(1, "Sample");
  201. // *************************************************
  202. ldc.i4.1// load constant 1 onto
  203. // stack
  204. ldstr "Sample"// load string constant
  205. // onto stack
  206. // create new object SampleClass passing 2 parameters
  207. // from stack.
  208. // Load reference to created object onto stack
  209. newobj instance voidSampleClass::.ctor(int32,string)
  210. stloc.0// store to variable 0
  211. // *************************************************
  212. // Access static class member
  213. // Console.WriteLine(SampleClass.nStatic.ToString());
  214. // *************************************************
  215. // Load the address of the static field on the stack
  216. ldsflda int32 SampleClass::nStatic
  217. // call Int32::ToString for object from stack
  218. call instance string[mscorlib]System.Int32
  219. ::ToString()
  220. // call static WriteLine passing string from stack
  221. // as parameter
  222. call void[mscorlib]System.Console
  223. ::WriteLine(string)
  224.  
  225. // *************************************************
  226. // Call non-static class function
  227. // Console.WriteLine(o.Number.ToString());
  228. // *************************************************
  229. ldloc.0// load variable 0
  230. // call function for object from stack
  231. call instance int32 SampleClass::get_Number()
  232. stloc.1// store to variable 1
  233. ldloca.s tmp // load address to stack
  234. call instance string[mscorlib]System.Int32
  235. ::ToString()
  236. call void[mscorlib]System.Console
  237. ::WriteLine(string)
  238.  
  239. // *************************************************
  240. // Call non-static class member
  241. // Console.WriteLine(o.String);
  242. // *************************************************
  243. ldloc.0
  244. callvirt instance stringSampleClass::get_String()
  245. call void[mscorlib]System.Console
  246. ::WriteLine(string)
  247.  
  248. // *************************************************
  249. ldstr "Press Enter to continue"
  250. call void[mscorlib]System.Console
  251. ::WriteLine(classSystem.String)
  252. call int32 [mscorlib]System.Console::Read()
  253. pop
  254. // *************************************************
  255.  
  256. ret
  257. }
  258. }

Exception Program

The program divides two numbers, catching a divide-by-zero exception. The try/catch block in MSIL looks like it does in C#.

Command:

  • leave.s label—leaves a protected block such as try or catch.

Code:

  1. .assembly Exception{}
  2.  
  3. /*
  4. int x, y, z;
  5. string s;
  6. Console.WriteLine("Enter x:");
  7. s = Console.ReadLine();
  8. x = Int32.Parse(s);
  9. Console.WriteLine("Enter y:");
  10. s = Console.ReadLine();
  11. y = Int32.Parse(s);
  12. try
  13. {
  14. z = x / y;
  15. Console.WriteLine(z.ToString());
  16. }
  17. catch (Exception e)
  18. {
  19. Console.WriteLine(e.Message);
  20. }
  21. */
  22.  
  23. .method staticpublicvoid main() il managed
  24. {
  25. .entrypoint
  26. .maxstack 8
  27.  
  28. .locals ([0] int32 x,
  29. [1] int32 y,
  30. [2] int32 z,
  31. [3]string s,
  32. [4]class[mscorlib]System.Exception e)
  33. // Enter x, y ...
  34.  
  35. .try
  36. {
  37. // *************************************************
  38. // z = x / y;
  39. // *************************************************
  40. ldloc.0// load var. 0
  41. ldloc.1// load var. 1
  42. div // divide
  43. stloc.2// store in var. 2
  44. // *************************************************
  45. // Console.WriteLine(z.ToString());
  46. // *************************************************
  47. ldloca.s z // load address of z
  48. call instance string[mscorlib]System.Int32
  49. ::ToString()
  50. call void[mscorlib]System.Console
  51. ::WriteLine(string)
  52.  
  53. leave.s END_TRY_CATCH // exit try block
  54. }
  55. catch[mscorlib]System.Exception
  56. {
  57. stloc.s e // store exception thrown on
  58. // the stack
  59. // *************************************************
  60. // Console.WriteLine(e.Message);
  61. // *************************************************
  62. ldloc.s e // load e
  63. callvirt instance string[mscorlib]System.Exception
  64. ::get_Message()
  65. call void[mscorlib]System.Console
  66. ::WriteLine(string)
  67. leave.s END_TRY_CATCH // exit catch block
  68. }
  69.  
  70. END_TRY_CATCH:
  71.  
  72. ret
  73. }

Downloads

Download source - 43 Kb

posted @ 2012-10-25 23:19  ppshinebl  阅读(162)  评论(0编辑  收藏  举报