In this chapter, I’ll talk about the different opcodes that are available for the CIL developer. I’ll start by showing you how the virtual execution stack works in .NET and how the opcodes are structured. Next, I’ll move on to some basic opcodes that allow you to load and store information into variables. Then you’ll see how to perform operations on these variables via CIL. Finally, I’ll wrap up by demonstrating how to create objects and call virtual and nonvirtual methods on objects.


Local Init Fault Block Exception Handling Implementation Code Code Snippet 
These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.


Unable to display preview. Download preview PDF.

Unable to display preview. Download preview PDF.


  1. 1.
    Native ints are usually used for calling methods via a method pointer; I’ll show you how to do this later on in the section “Method Pointers.”Google Scholar
  2. 2.
    NET uses the IEC 60559:1989 standard to handle floating-point operations and encodings.Google Scholar
  3. 3.
    See Section 1.1.4 of Partition III for more details on pointers.Google Scholar
  4. 4.
    See Section 1.2.1 of Partition III for information on reserved and experimental byte values.Google Scholar
  5. 5.
    I’ve inserted the C# code to help illustrate what the CIL code is doing.Google Scholar
  6. 6.
    A Boolean takes up 1 byte—see Section 1.1.2 in Partition III. Actually, you could set a Boolean to any nonzero value to make it true, so ldc. i4.7 would work just as well.Google Scholar
  7. 7.
    See Section 3.63 of Partition III.8. Note that you can use ldfld and stfld to load and store static fields. However, you cannot use ldsfld and stsfld for instance fields.Google Scholar
  8. 9.
    Those who remember Reverse-Polish Notation calculators will have no problems writing CIL-based mathematical code.Google Scholar
  9. 10.
    This only happens for integral types; it doesn’t happen with F types. See 3.31 of Partition III for more details.Google Scholar
  10. 11.
    With callvirt and call being so close in behavior, you may wonder why there are two different opcodes. I’ll cover that discussion in Chapter 6.Google Scholar
  11. 12.
    If you’re calling an instance method via a function pointer, though, you need to make sure the correct instance reference is on the stack.Google Scholar
  12. 13.
    Section 3.20 of Partition III seems to suggest that this opcode could be verifiable, but for some reason, the first release considers it nonverifiable in all cases. This may change in the future.Google Scholar
  13. 14.
    See Section 3.42 of Partition III for all of the opcodes—it’s the same as cony.Google Scholar
  14. 15.
    Note, though, that nonzero-based arrays are not CLS compliant.Google Scholar
  15. 16.
    Also available are brinst, brnull, and brzero, but they’re just aliases to brtrue and brfalsesee Section 3.17 of Partition III for more details.Google Scholar
  16. 17.
    Technically, the object to be thrown can be any object; it does not need to inherit from Exception. However, see Section 4.29 of Partition III to find out why you may not want to do this.Google Scholar
  17. 18.
    Of course, this dialog box may look different depending on the debuggers (if any) loaded on the target machine.Google Scholar
  18. 19.
    Note that endfinally and endfault are the same opcode—see Section 3.35 of Partition III.Google Scholar
  19. 20.
    By this time, some of the direct references to locals with ldloc and stloc will be off; the source code that you can download uses variable names instead of index values.Google Scholar

Copyright information

© Jason Bock 2002

Authors and Affiliations

  • Jason Bock

There are no affiliations available

Personalised recommendations