Common Language Infrastructure (CLI)

Partition II:
Metadata Definition and Semantics

 

Table of contents

 

1                  Scope  8

2                  Overview  9

3                  Validation and Verification  10

4                  Introductory Examples  11

4.1              Hello World Example  11

4.2              Examples  11

5                  General Syntax  12

5.1              General Syntax Notation  12

5.2              Terminals  12

5.3              Identifiers  13

5.4              Labels and Lists of Labels  14

5.5              Lists of Hex Bytes  15

5.6              Floating point numbers  15

5.7              Source Line Information  15

5.8              File Names  15

5.9              Attributes and Metadata  15

5.10            ilasm Source Files  15

6                  Assemblies, Manifests and Modules  15

6.1              Overview of Modules, Assemblies, and Files  15

6.2              Defining an Assembly  15

6.2.1          Information about the Assembly (<asmDecl>) 15

6.2.2          Manifest Resources  15

6.2.3          Files in the Assembly  15

6.3              Referencing Assemblies  15

6.4              Declaring Modules  15

6.5              Referencing Modules  15

6.6              Declarations inside a Module or Assembly  15

6.7              Exported Type Definitions  15

7                  Types and Signatures  15

7.1              Types  15

7.1.1          modreq and modopt 15

7.1.2          pinned  15

7.2              Built-in Types  15

7.3              References to User-defined Types (<typeReference>) 15

7.4              Native Data Types  15

8                  Visibility, Accessibility and Hiding  15

8.1              Visibility of Top-Level Types and Accessibility of Nested Types  15

8.2              Accessibility  15

8.3              Hiding  15

9                  Defining Types  15

9.1              Type Header (<classHead>) 15

9.1.1          Visibility and Accessibility Attributes  15

9.1.2          Type Layout Attributes  15

9.1.3          Type Semantics Attributes  15

9.1.4          Inheritance Attributes  15

9.1.5          Interoperation Attributes  15

9.1.6          Special Handling Attributes  15

9.2              Body of a Type Definition  15

9.3              Introducing and Overriding Virtual Methods  15

9.3.1          Introducing a Virtual Method  15

9.3.2          The .override Directive  15

9.3.3          Accessibility and Overriding  15

9.4              Method Implementation Requirements  15

9.5              Special Members  15

9.5.1          Instance constructors  15

9.5.2          Instance Finalizer 15

9.5.3          Type Initializer 15

9.6              Nested Types  15

9.7              Controlling Instance Layout 15

9.8              Global Fields and Methods  15

10                Semantics of Classes  15

11                Semantics of Interfaces  15

11.1             Implementing Interfaces  15

11.2             Implementing Virtual Methods on Interfaces  15

12                Semantics of Value Types  15

12.1             Referencing Value Types  15

12.2             Initializing Value Types  15

12.3             Methods of Value Types  15

13                Semantics of Special Types  15

13.1             Vectors  15

13.2             Arrays  15

13.3             Enums  15

13.4             Pointer Types  15

13.4.1         Unmanaged Pointers  15

13.4.2         Managed Pointers  15

13.5             Method Pointers  15

13.6             Delegates  15

13.6.1         Synchronous Calls to Delegates  15

13.6.2         Asynchronous Calls to Delegates  15

14                Defining, Referencing, and Calling Methods  15

14.1             Method Descriptors  15

14.1.1         Method Declarations  15

14.1.2         Method Definitions  15

14.1.3         Method References  15

14.1.4         Method Implementations  15

14.2             Static, Instance, and Virtual Methods  15

14.3             Calling Convention  15

14.4             Defining Methods  15

14.4.1         Method Body  15

14.4.2         Predefined Attributes on Methods  15

14.4.3         Implementation Attributes of Methods  15

14.4.4         Scope Blocks  15

14.4.5         vararg Methods  15

14.5             Unmanaged Methods  15

14.5.1         Method Transition Thunks  15

14.5.2         Platform Invoke  15

14.5.3         Via Function Pointers  15

14.5.4         COM Interop  15

14.5.5         Data Type Marshaling  15

14.5.6         Managed Native Calling Conventions (x86) 15

15                Defining and Referencing Fields  15

15.1             Attributes of Fields  15

15.1.1         Accessibility Information  15

15.1.2         Field Contract Attributes  15

15.1.3         Interoperation Attributes  15

15.1.4         Other Attributes  15

15.2             Field Init Metadata  15

15.3             Embedding Data in a PE File  15

15.3.1         Data Declaration  15

15.3.2         Accessing Data from the PE File  15

15.3.3         Unmanaged Thread-local Storage  15

15.4             Initialization of Non-Literal Static Data  15

15.4.1         Data Known at Link Time  15

15.5             Data Known at Load Time  15

15.5.1         Data Known at Run Time  15

16                Defining Properties  15

17                Defining Events  15

18                Exception Handling  15

18.1             Protected Blocks  15

18.2             Handler Blocks  15

18.3             Catch  15

18.4             Filter 15

18.5             Finally  15

18.6             Fault Handler 15

19                Declarative Security  15

20                Custom Attributes  15

20.1             CLS Conventions: Custom Attribute Usage  15

20.2             Attributes Used by the CLI 15

20.2.1         Pseudo Custom Attributes  15

20.2.2         Custom Attributes Defined by the CLS  15

20.2.3         Custom Attributes for CIL-to-Native-Code Compiler and Debugger 15

20.2.4         Custom Attributes for Remoting  15

20.2.5         Custom Attributes for Security  15

20.2.6         Custom Attributes for TLS  15

20.2.7         Pseudo Custom Attributes for the Assembly Linker 15

20.2.8         Custom Attributes Provided for Interoperation with Unmanaged Code  15

20.2.9         Custom Attributes, Various  15

21                Metadata Logical Format: Tables  15

21.1             Metadata Validation Rules  15

21.2             Assembly : 0x20  15

21.3             AssemblyOS : 0x22  15

21.4             AssemblyProcessor : 0x21  15

21.5             AssemblyRef : 0x23  15

21.6             AssemblyRefOS : 0x25  15

21.7             AssemblyRefProcessor : 0x24  15

21.8             ClassLayout : 0x0F  15

21.9             Constant : 0x0B  15

21.10           CustomAttribute : 0x0C  15

21.11           DeclSecurity : 0x0E  15

21.12           EventMap : 0x12  15

21.13           Event : 0x14  15

21.14           ExportedType : 0x27  15

21.15           Field : 0x04  15

21.16           FieldLayout : 0x10  15

21.17           FieldMarshal : 0x0D   15

21.18           FieldRVA : 0x1D   15

21.19           File : 0x26  15

21.20           ImplMap : 0x1C  15

21.21           InterfaceImpl : 0x09  15

21.22           ManifestResource : 0x28  15

21.23           MemberRef : 0x0A   15

21.24           Method : 0x06  15

21.25           MethodImpl : 0x19  15

21.26           MethodSemantics : 0x18  15

21.27           Module : 0x00  15

21.28           ModuleRef : 0x1A   15

21.29           NestedClass : 0x29  15

21.30           Param : 0x08  15

21.31           Property : 0x17  15

21.32           PropertyMap : 0x15  15

21.33           StandAloneSig : 0x11  15

21.34           TypeDef : 0x02  15

21.35           TypeRef : 0x01  15

21.36           TypeSpec : 0x1B  15

22                Metadata Logical Format: Other Structures  15

22.1             Bitmasks and Flags  15

22.1.1         Values for AssemblyHashAlgorithm   15

22.1.2         Values for AssemblyFlags  15

22.1.3         Values for Culture  15

22.1.4         Flags for Events [EventAttributes] 15

22.1.5         Flags for Fields [FieldAttributes] 15

22.1.6         Flags for Files [FileAttributes] 15

22.1.7         Flags for ImplMap [PInvokeAttributes] 15

22.1.8         Flags for ManifestResource [ManifestResourceAttributes] 15

22.1.9         Flags for Methods [MethodAttributes] 15

22.1.10       Flags for Methods [MethodImplAttributes] 15

22.1.11       Flags for MethodSemantics [MethodSemanticsAttributes] 15

22.1.12       Flags for Params [ParamAttributes] 15

22.1.13       Flags for Properties [PropertyAttributes] 15

22.1.14       Flags for Types [TypeAttributes] 15

22.1.15       Element Types used in Signatures  15

22.2             Blobs and Signatures  15

22.2.1         MethodDefSig  15

22.2.2         MethodRefSig  15

22.2.3         StandAloneMethodSig  15

22.2.4         FieldSig  15

22.2.5         PropertySig  15

22.2.6         LocalVarSig  15

22.2.7         CustomMod  15

22.2.8         TypeDefOrRefEncoded  15

22.2.9         Constraint 15

22.2.10       Param   15

22.2.11       RetType  15

22.2.12       Type  15

22.2.13       ArrayShape  15

22.2.14       TypeSpec  15

22.2.15       Short Form Signatures  15

22.3             Custom Attributes  15

22.4             Marshalling Descriptors  15

23                Metadata Physical Layout 15

23.1             Fixed Fields  15

23.2             File Headers  15

23.2.1         Metadata root 15

23.2.2         Stream Header 15

23.2.3         #Strings heap  15

23.2.4         #US and #Blob heaps  15

23.2.5         #GUID heap  15

23.2.6         #~ stream   15

23.2.7         Coded Indexes  15

24                File Format Extensions to PE  15

24.1             Structure of the Runtime File Format 15

24.2             PE Headers  15

24.2.1         MS-DOS Header 15

24.2.2         PE File Header 15

24.2.3         PE Optional Header 15

24.3             Section Headers  15

24.3.1         Import Table and Import Address Table (IAT) 15

24.3.2         Relocations  15

24.3.3         CLI Header 15

24.4             Common Intermediate Language Physical Layout 15

24.4.1         Method Header Type Values  15

24.4.2         Tiny Format 15

24.4.3         Fat Format 15

24.4.4         Flags for Method Headers  15

24.4.5         Method Data Section  15

24.4.6         Exception Handling Clauses  15

 


1         Scope

Partition I_alink=Partition_I of the Common Language Infrastructure (CLI) describes the overall architecture of the CLI, and provides the normative description of the Common Type System (CTS), the Virtual Execution System (VES), and the Common Language Specification (CLS).  It also provides a non-normative description of the metadata and a comprehensive set of abbreviations, acronyms (Partition I_alink=Partition_I) and definitions, included by reference (Partition I_alink=Partition_I) from all other Partitions.

Partition II (this specification) provides the normative description of the metadata: its physical layout (as a file format), its logical contents (as a set of tables and their relationships), and its semantics (as seen from a hypothetical assembler, ilasm).

2         Overview

This document focuses on the structure and semantics of metadata. The semantics of metadata, which dictate much of the operation of the VES, are described using the syntax of ilasm, an assembler language for CIL.  The ilasm syntax itself is considered a normative part of this ECMA standard.  This constitutes Chapters 5_5_General_Syntax through 20_20_Custom_Attributes. A complete syntax for ilasm is included in Partition V_alink=Partition_V. The structure (both logical and physical) is covered in Chapters 21_21_Metedata_Logical_Format_Tables through 24_24_File_Format_Extensions_to_PE.

Rationale: An assembly language is really just syntax for specifying the metadata in a file and the CIL instructions in that file.   Specifying ilasm provides a means of interchanging programs written directly for the CLI without the use of a higher-level language and also provides a convenient way to express examples.

The semantics of the metadata also can be described independently of the actual format in which the metadata is stored.  This point is important because the storage format as specified Chapters 21_21_Metedata_Logical_Format_Tables through 24_24_File_Format_Extensions_to_PE is engineered to be efficient for both storage space and access time but this comes at the cost of the simplicity desirable for describing its semantics.

3         Validation and Verification

Validation refers to a set of tests that can be performed on any file to check that the file format, metadata, and CIL are self-consistent. These tests are intended to ensure that the file conforms to the mandatory requirements of this specification.  The behavior of conforming implementations of the CLI when presented with non-conforming files is unspecified.

Verification refers to a check of both CIL and its related metadata to ensure that the CIL code sequences do not permit any access to memory outside the program’s logical address space. In conjunction with the validation tests, verification ensures that the program cannot access memory or other resources to which it is not granted access.

Partition III_alink=Partition_III specifies the rules for both valid and verifiable use of CIL instructions.  Partition III_alink=Partition_III also provides an informative description of rules for validating the internal consistency of metadata (the rules follow, albeit indirectly, from the specification in this Partition) as well as containing a normative description of the verification algorithm.  A mathematical proof of soundness of the underlying type system is possible, and provides the basis for the verification requirements.  Aside from these rules this standard does not specify:

·              at what time (if ever) such an algorithm should be performed

·              what a conforming implementation should do in case of failure of verification. 

The following graph makes this relationship clearer (see next paragraph for a description):

Figure 1: Relationship between valid and verifiable CIL

In the above figure, the outer circle contains all code permitted by the ilasm syntax. The next circle represents all code that is valid CIL. The dotted inner circle represents all type safe code.  Finally, the black innermost circle contains all code that is verifiable.  (The difference between typesafe code and verifiable code is one of provability: code which passes the VES verification algorithm is, by-definition, verifiable; but that simple algorithm rejects certain code, even though a deeper analysis would reveal it as genuinely typesafe).  Note that even if a program follows the syntax described in Partition V_alink=Partition_V, the code may still not be valid, because valid code shall adhere to restrictions presented in this document and in Partition III_alink=Partition_III.

Verification is a very stringent test. There are many programs that will pass validation but will fail verification. The VES cannot guarantee that these programs do not access memory or resources to which they are not granted access. Nonetheless, they may have been correctly constructed so that they do not access these resources. It is thus a matter of trust, rather than mathematical proof, whether it is safe to run these programs. A conforming implementation of the CLI may allow unverifiable code (valid code that does not pass verification) to be executed, although this may be subject to administrative trust controls that are not part of this standard.  A conforming implementation of the CLI shall allow the execution of verifiable code, although this may be subject to additional implementation-specified trust controls.


4         Introductory Examples

This section and its subsections contain only informative text.

Before diving into the details, it is useful to see an introductory sample program to get a feeling for the ilasm assembly language. The next section shows the famous Hello World program, this time in the ilasm assembly language.

4.1         Hello World Example

This section gives a simple example to illustrate the general feel of ilasm. Below is code that prints the well known “Hello world!” salutation. The salutation is written by calling WriteLine, a static method found in the class System.Console that is part of the assembly mscorlib (see Partition IV_alink=Partition_IV).

Example (informative):

.assembly extern mscorlib {}

.assembly hello {}

.method static public void main() cil managed

{ .entrypoint

  .maxstack 1

  ldstr "Hello world!"

  call void [mscorlib]System.Console::WriteLine(class System.String)

  ret

}

The .assembly extern declaration references an external assembly, mscorlib, which defines System.Console. The .assembly declaration in the second line declares the name of the assembly for this program.  (Assemblies are the deployment unit for executable content for the CLI.)  The .method declaration defines the global method main.   The body of the method is enclosed in braces.  The first line in the body indicates that this method is the entry point for the assembly (.entrypoint), and the second line in the body specifies that it requires at most one stack slot (.maxstack).

The method contains only three instructions. The ldstr instruction pushes the string constant "Hello world!" onto the stack and the call instruction invokes System.Console::WriteLine, passing the string as its only argument (note that string literals in CIL are instances of the standard class System.String). As shown, call instructions shall include the full signature of the called method. Finally, the last instruction returns (ret) from main.

4.2         Examples

This document contains integrated examples for most features of the CLI metadata. Many sections conclude with an example showing a typical use of the feature. All these examples are written using the ilasm assembly language.  In addition, Partition V_alink=Partition_V contains a longer example of a program written in the ilasm assembly language.  All examples are, of course, informative only.

End informative text


5         General Syntax

This section describes aspects of the ilasm syntax that are common to many parts of the grammar.  The term “ASCII” refers to the American Standard Code for Information Interchange, a standard seven-bit code that was proposed by ANSI in 1963, and finalized in 1968.  The ASCII repertoire of Unicode is the set of 128 Unicode characters from U+0000 to U+007F.

5.1         General Syntax Notation

This document uses a modified form of the BNF syntax notation. The following is a brief summary of this notation.

Bold items are terminals. Items placed in angle brackets (e.g. <int64>) are names of syntax classes and shall be replaced by actual instances of the class. Items placed in square brackets (e.g. [<float>]) are optional, and any item followed by * can appear zero or more times. The character “|” means that the items on either side of it are acceptable. The options are sorted in alphabetical order (to be more specific: in ASCII order, ignoring “<” for syntax classes, and case-insensitive). If a rule starts with an optional term, the optional term is not considered for sorting purposes.

ilasm is a case-sensitive language. All terminals shall be used with the same case as specified in this reference.

Example (informative):

A grammar such as

<top> ::= <int32> | float <float> |

          floats [<float> [, <float>]*] | else <QSTRING>

would consider the following all to be legal:

     12

     float 3

     float –4.3e7

     floats

     floats 2.4

     floats 2.4, 3.7

     else "Something \t weird"

but all of the following to be illegal:

     else 3

     3, 4

     float 4.3, 2.4

     float else

     stuff

5.2         Terminals

The basic syntax classes used in the grammar are used to describe syntactic constraints on the input intended to convey logical restrictions on the information encoded in the metadata.

The syntactic constraints described in this clause are informative only.  The semantic constraints (e.g. “shall be represented in 32 bits”) are normative.

<int32> is either a decimal number or “0x” followed by a hexadecimal number, and shall be represented in 32 bits.

<int64> is either a decimal number or “0x” followed by a hexadecimal number, and shall be represented in 64 bits.

<hexbyte> is a 2-digit hexadecimal number that fits into one byte.

<realnumber> is any syntactic representation for a floating point number that is distinct from that for all other terminal nodes.  In this document, a period (.) is used to separate the integer and fractional parts, and “e” or “E” separates the mantissa from the exponent.  Either (but not both) may be omitted.

Note: A complete assembler may also provide syntax for infinities and NaNs.

<QSTRING> is a string surrounded by double quote (″) marks. Within the quoted string the character “\” can be used as an escape character, with “\t” for a tab character, “\n” for a new line character, or followed by three octal digits in order to insert an arbitrary byte into the string. The “+” operator can be used to concatenate string literals. This way, a long string can be broken across multiple lines by using “+” and a new string on each line. An alternative is using “\” as the last character in a line, in which case the line break is not entered into the generated string. Any white characters (space, line feed, carriage return, and tab) between the “\” and the first character on the next line are ignored. See also examples below.

Note: A complete assembler will need to deal with the full set of issues required to support Unicode encodings, see Partition I_alink=Partition_I (especially CLS Rule 4).

<SQSTRING> is similar to <QSTRING> with the difference that it is surround by single quote (′) marks instead of double quote marks.

<ID> is a contiguous string of characters which starts with either an alphabetic character or one of “_”, “$”, “@” or “?” and is followed by any number of alphanumeric characters or any of “_”, “$”,  “@”, or “?”. An <ID> is used in only two ways:

·              As a label of a CIL instruction

·              As an <id> which can either be an <ID> or an <SQSTRING>, so that special characters can be included.

Example (informative):

The following examples shows breaking of strings:

    ldstr "Hello " + "World " +

    "from CIL!"

and

    ldstr "Hello World\

       \040from CIL!"

become both "Hello World from CIL!".

5.3         Identifiers

Identifiers are used to name entities. Simple identifiers are just equivalent to an <ID>. However, the ilasm syntax allows the use of any identifier that can be formed using the Unicode character set (see Partition I_alink=Partition_I). To achieve this an identifier is placed within single quotation marks. This is summarized in the following grammar.

<id> ::=

  <ID>

   | <SQSTRING>

 

Keywords may only be used as identifiers if they appear in single quotes (see Partition V_alink=Partition_V for a list of all keywords).

Several <id>’s may be combined to form a larger <id>. The <id>’s are separated by a dot (.). An <id> formed in this way is called a <dottedname>.

<dottedname> ::= <id> [. <id>]*

 

Rationale: <dottedname> is provided for convenience, since “.” can be included in an <id> using the <SQSTRING> syntax.  <dottedname> is used in the grammar where “.” is considered a common character (e.g. fully qualified type names)

Implementation Specific (Microsoft)

Names that end with $PST followed by a hexadecimal number have a special meaning. The assembler will automatically truncate the part starting with the $PST. This is in support of compiler-controlled accessibility, see Partition I_alink=Partition_V.  Also, the first release of the CLI limits the length of identifiers;­­ see Chapter 21_21_Metedata_Logical_Format_Tables for details.

 Examples (informative):

The following shows some simple identifiers:

     A         

     Test      

     $Test     

     @Foo?     

     ?_X_

The following shows identifiers in single quotes:

     ′Weird Identifier′      

     ′Odd\102Char′    

     ′Embedded\nReturn′

The following shows dotted names:

     System.Console

     A.B.C

     ′My Project′.′My Component′.′My Name′

5.4         Labels and Lists of Labels

Labels are provided as a programming convenience; they represent a number that is encoded in the metadata.  The value represented by a label is typically an offset in bytes from the beginning of the current method, although the precise encoding differs depending on where in the logical metadata structure or CIL stream the label occurs.  For details of how labels are encoded in the metadata, see Chapters 21_21_Metedata_Logical_Format_Tables through 24_24_File_Format_Extensions_to_PE; for their encoding in CIL instructions see Partition III_alink=Partition_III.

A simple label is a special name that represents an address. Syntactically, a label is equivalent to an <id>. Thus, labels may be also single quoted and may contain Unicode characters.

A list of labels is comma separated, and can be any combination of these simple labels.

<labeloroffset> ::= <id>

<labels> ::= <labeloroffset> [, <labeloroffset>]*

 

Rationale: In a real assembler the syntax for <labeloroffset> might allow the direct specification of a number rather than requiring symbolic labels.

Implementation Specific (Microsoft)

The following syntax is also supported, for round-tripping purposes:

<labeloroffset> ::= <int32> | <label>

ilasm distinguishes between two kinds of labels: code labels and data labels. Code labels are followed by a colon (“:”) and represent the address of an instruction to be executed. Code labels appear before an instruction and they represent the address of the instruction that immediately follows the label. A particular code label name may not be declared more than once in a method.

In contrast to code labels, data labels specify the location of a piece of data and do not include the colon character. The data label may not be used as a code label, and a code label may not be used as a data label. A particular code label name may not be declared more than once in a module.

<codeLabel> ::= <id> :

<dataLabel> ::= <id>

 

Example (informative):

The following defines a code label, ldstr_label, that represents the address of the ldstr instruction:

ldstr_label:    ldstr  "A label"

5.5         Lists of Hex Bytes

A list of bytes consists simply of one or more hex bytes. Hex bytes are pairs of characters 0 – 9, a – f, and A – F.

<bytes> ::= <hexbyte> [<hexbyte>*]

 

5.6         Floating point numbers

There are two different ways to specify a floating-point number:

1.             Use the dot (“.”) for the decimal point and “e” or “E” in front of the exponent. Both the decimal point and the exponent are optional.

2.             Indicate that the floating-point value is derived from an integer using the keyword float32 or float64 and indicating the integer in parentheses.

<float64> ::=

  float32 ( <int32> )

| float64 ( <int64> )

| <realnumber>

 

Example (informative):

5.5

1.1e10

float64(128)    // note: this converts the integer 128 to its fp value

5.7         Source Line Information

The metadata does not encode information about the lexical scope of variables or the mapping from source line numbers to CIL instructions. Nonetheless, it is useful to specify an assembler syntax for providing this information for use in creating alternate encodings of the information.

Implementation Specific (Microsoft)

Source line information is stored in the PDB (Portable Debug) file associated with each module.

.line takes a line number, and optional column number (preceded by a colon) and single quoted string that specifies the name of the file the line number is referring to

<externSourceDecl> ::= .line <int32> [ : <int32> ] [<SQSTRING>]

 

Implementation Specific (Microsoft)

For compatibility reasons, ilasm allows the following:

<externSourceDecl> ::= … | #line <int32> <QSTRING>

Notice that this requires the file name and that it shall be double quoted, not single quoted as with .line

5.8         File Names

Some grammar elements require that a file name be supplied. A file name is like any other name where “.” is considered a normal constituent character. The specific syntax for file names follows the specifications of the underlying operating system.

<filename> ::=

Section

  <dottedname>

5.3_5.3_Identifiers

 

5.9         Attributes and Metadata

Attributes of types and their members attach descriptive information to their definition. The most common attributes are predefined and have a specific encoding in the metadata associated with them (see Chapter 22_22_Metadata_Logical_Format:_Other_Structures).  In addition, the metadata provides a way of attaching user-defined attributes to metadata, using several different encodings.

From a syntactic point of view, there are several ways for specifying attributes in ilasm:

·              Using special syntax built into ilasm. For example the keyword private in a <classAttr> specifies that the visibility attribute on a type should be set to allow access only within the defining assembly.

·              Using a general-purpose syntax in ilasm.  The non-terminal <customDecl> describes this grammar (see Chapter 20_20_Custom_Attributes). For some attributes, called pseudo-custom attributes, this grammar actually results in setting special encodings within the metadata (see clause 20.2.1_20.2.1_Pseudo_Custom_Attributes).

·              Some attributes are required to be set based on the settings of other attributes or information within the metadata and are not visible from the syntax of ilasm at all.  These attributes, called hidden attributes

·              Security attributes are treated specially.  There is special syntax in ilasm that allows the XML representing security attributes to be described directly (see Chapter 19_19_Declarative_Security).  While all other attributes defined either in the standard library or by user-provided extension are encoded in the metadata using one common mechanism described in Section 21.10_21.9_CustomAttribute_:_0x0C, security attributes (distinguished by the fact that they inherit, directly or indirectly from System.Security.Permissions.SecurityAttribute, see Partition IV_alink=Partition_IV) shall be encoded as described in Section 21.11_21.10_DeclSecurity_:_0x0E.

5.10      ilasm Source Files

An input to ilasm is a sequence of declarations, defined as follows:

<ILFile> ::=

Reference

 <decl>*

5.10_5.10_ilasm_source_files

 

The complete grammar for a top level declaration is shown below. The following sections will concentrate on the various parts of this grammar.

<decl> ::=

Reference

  .assembly <dottedname> { <asmDecl>* }

6.1

| .assembly extern <dottedname> { <asmRefDecl>* }

6.3

| .class <classHead> { <classMember>* }

9

| .class extern <exportAttr> <dottedname> { <externClassDecl>* }

6.7

| .corflags <int32>

6.1

| .custom <customDecl>

20

| .data <datadecl>

15.3.1

| .field <fieldDecl>

15

| .file [nometadata] <filename> [.hash = ( <bytes> )]
        [.entrypoint ]

6.2.3

| .mresource [public | private] <dottedname>
             [( <QSTRING> )] { <manResDecl>* }

6.2.2

| .method <methodHead> { <methodBodyItem>* }

14

| .module [<filename>]

6.4

| .module extern <filename>

6.5

| .subsystem <int32>

6.2

| .vtfixup <vtfixupDecl>

14.5.1

| <externSourceDecl>

5.7

| <securityDecl>

18

 

Implementation Specific (Microsoft)

The grammar for declarations also includes the following.  These are described in a separate product specification.

Implementation Specific (Microsoft)

<decl> ::=

Reference

.file alignment <int32>

 

| .imagebase <int64>

 

| .language <languageDecl>

 

| .namespace <id>

 

|

 

 

6         Assemblies, Manifests and Modules

Assemblies and modules are grouping constructs, each playing a different role in the CLI.

An assembly is a set of one or more files deployed as a unit.  An assembly always contains a manifest that specifies (see Section 6.1):

·              Version, name, culture, and security requirements for the assembly.

·              Which other files, if any, belong to the assembly along with a cryptographic hash of each file.  The manifest itself resides in the metadata part of a file and that file is always part of the assembly.

·              Which of the types defined in other files of the assembly are to be exported from the assembly.  Types defined in the same file as the manifest are exported based on attributes of the type itself.

·              Optionally, a digital signature for the manifest itself and the public key used to compute it.

A module is a single file containing executable content in the format specified here.  If the module contains a manifest then it also specifies the modules (including itself) that constitute the assembly.  An assembly shall contain only one manifest amongst all its constituent files. For an assembly to be executed (rather than dynamically loaded) the manifest shall reside in the module that contains the entry point.

While some programming languages introduce the concept of a namespace, there is no support in the CLI for this concept.  Type names are always specified by their full name relative to the assembly in which they are defined.


6.1         Overview of Modules, Assemblies, and Files

This section contains informative text only.

The following picture should clarify the various forms of references:

Figure 2: References

Eight files are shown in the picture. The name of each file is shown below the file. Files that declare a module have an additional border around them and have names beginning with M. The other two files have a name beginning with F. These files may be resource files, like bitmaps, or other files that do not contain CIL code.

Files M1 and M4 declare an assembly in addition to the module declaration, namely assemblies A and B, respectively. The assembly declaration in M1 and M4 references other modules, shown with straight lines. Assembly A references M2 and M3. Assembly B references M3 and M5. Thus, both assemblies reference M3.

Usually, a module belongs only to one assembly, but it is possible to share it across assemblies. When Assembly A is loaded at runtime, an instance of M3 will be loaded for it. When Assembly B is loaded into the same application domain, possibly simultaneously with Assembly A, M3 will be shared for both assemblies. Both assemblies also reference F2, for which similar rules apply.

The module M2 references F1, shown by dotted lines. As a consequence F1 will be loaded as part of Assembly A, when A is executed. Thus, the file reference shall also appear with the assembly declaration. Similarly, M5 references another module, M6, which becomes part of B when B is executed. It follows, that assembly B shall also have a module reference to M6.

End informative text


6.2         Defining an Assembly

An assembly is specified as a module that contains a manifest in the metadata; see Section 21.2.  The information for the manifest is created from the following portions of the grammar: 

<decl> ::=

Section

  .assembly <dottedname> { <asmDecl>* }

6.2

| .assembly extern <dottedname> { <asmRefDecl>* }

6.3

| .corflags <int32>

6.2

| .file [nometadata] <filename> .hash = ( <bytes> )
        [.entrypoint ]

6.2.3

| .module extern <filename>

6.5

| .mresource [public | private] <dottedname>

            [( <QSTRING> )] { <manResDecl>* }

6.2.2

| .subsystem <int32>

6.2

| …

 

 

The .assembly directive declares the manifest and specifies to which assembly the current module belongs. A module shall contain at most one .assembly directive. The <dottedname> specifies the name of the assembly.

Note: Since some platforms treat names in a case insensitive manner, two assemblies that have names that differ only in case should not be declared.

The .corflags directive sets a field in the CLI header of the output PE file (see clause 24.3.3.1).  A conforming  implementation of the CLI shall expect it to be 1.  For backwards compatibility, the three least significant bits are reserved.  Future versions of this standard may provide definitions for values between 8 and 65,535. Experimental and non-standard uses should thus use values greater than 65,535.

The .subsystem directive is used only when the assembly is directly executed (as opposed to used as a library for another program).  It specifies the kind of application environment required for the program, by storing the specified value in the PE file header (see clause 24.2.2). While a full 32 bit integer may be supplied, a conforming implementation of the CLI need only respect two possible values:

If the value is 2, the program should be run using whatever conventions are appropriate for an application that has a graphical user interface.

If the value is 3, the program should be run using whatever conventions are appropriate for an application that has a direct console attached.

Implementation Specific (Microsoft)

<decl> ::= … | .file alignment <int32> | .imagebase <int64>

The .file alignment directive sets the file alignment field in the PE header of the output file.  Legal values are multiples of 512.  (Different sections of the PE file are aligned, on disk, at the specified value (in bytes))

The .imagebase directive sets the imagebase field in the PE header of the output file.  This value specifies the virtual address at which this PE file will be loaded into the process.

See clause 24.2.3.2

 

Example (informative):

.assembly CountDown

{ .hash algorithm 32772

  .ver 1:0:0:0

}

.file Counter.dll .hash = (BA D9 7D 77 31 1C 85 4C 26 9C 49 E7 02 BE E7 52 3A CB 17 AF)

6.2.1         Information about the Assembly (<asmDecl>)

The following grammar shows the information that can be specified about an assembly. 

<asmDecl> ::=

Description

Section

  .custom <customDecl>

Custom attributes

20

  .hash algorithm <int32>

Hash algorithm used in the .file directive

6.2.1.1

| .culture <QSTRING>

Culture for which this assembly is built

6.2.1.2

| .publickey = ( <bytes> )

The originator's public key.

6.2.1.3

| .ver <int32> : <int32> : <int32> : <int32>

Major version, minor version, revision, and build

6.2.1.4

| <securityDecl>

Permissions needed, desired, or prohibited

19

 

6.2.1.1          Hash Algorithm

<asmDecl> ::= .hash algorithm <int32> | …

 

When an assembly consists of more than one file (see clause 6.2.3), the manifest for the assembly specifies both the name of the file and the cryptographic hash of the contents of the file.  The algorithm used to compute the hash can be specified, and shall be the same for all files included in the assembly.  All values are reserved for future use, and conforming implementations of the CLI shall use the SHA1(see Partition I_alink=Partition_I)  hash function and shall specify this algorithm by using a value of 32772 (0x8004).

Rationale: SHA1 was chosen as the best widely available technology at the time of standardization (see Partition I_alink=Partition_I).   A single algorithm is chosen since all conforming implementations of the CLI would be required to implement all algorithms to ensure portability of executable images.

6.2.1.2          Culture

<asmDecl> ::= .culture <QSTRING> | …

 

When present, this indicates that the assembly has been customized for a specific culture.  The strings that shall be used here are those specified in Partition IV_alink=Partition_IV as acceptable with the class  System.Globalization.CultureInfo. When used for comparison between an assembly reference and an assembly definition these strings shall be compared in a case insensitive manner.

Implementation Specific (Microsoft)

The product version of ilasm and ildasm use .locale rather than .culture.

Note: The culture names follow the IETF RFC1766 names. The format is “<language>-<country/region>”, where <language> is a lowercase two-letter code in ISO 639-1. <country/region> is an uppercase two-letter code in ISO 3166

6.2.1.3          Originator’s Public Key

<asmDecl> ::= .publickey = ( <bytes> ) | …

 

The CLI metadata allows the producer of an assembly to compute a cryptographic hash of the assembly (using the SHA1 hash function) and then encrypt it using the RSA algorithm (see Partition I_alink=Partition_I) and a public/private key pair of the producer’s choosing.  The results of this (an “SHA1/RSA digital signature”) can then be stored in the metadata along with the public part of the key pair required by the RSA algorithm.  The .publickey directive is used to specify the public key that was used to compute the signature.  To calculate the hash, the signature is zeroed, the hash calculated, then the result stored into the signature.

A reference to an assembly (see Section 6.3) captures some of this information at compile time.  At runtime, the information contained in the assembly reference can be combined with the information from the manifest of the assembly located at runtime to ensure that the same private key was used to create both the assembly seen when the reference was created (compile time) and when it is resolved (runtime).

6.2.1.4          Version Numbers

<asmDecl> ::= .ver <int32> : <int32> : <int32> : <int32> | …

 

The version number of the assembly, specified as four 32-bit integers.  This version number shall be captured at compile time and used as part of all references to the assembly within the compiled module.  This standard places no other requirement on the use of the version numbers.

Note: A conforming implementation may ignore version numbers entirely, or it may require that they match precisely when binding a reference, or any other behavior deemed appropriate.  By convention:

the first of these is considered the major version number and assemblies with the same name but different major versions are not interchangeable.  This would be appropriate, for example, for a major rewrite of a product where backwards compatibility cannot be assumed.

the second of these is considered the minor version number and assemblies with the same name and major version but different minor versions indicate significant enhancements but with intention to be backward compatible.  This would be appropriate, for example, on a “point release” of a product or a fully backward compatible new version of a product.

the third of these is considered the revision number and assemblies with the same name, major and minor version number but different revisions are intended to be fully interchangeable. This would be appropriate, for example, to fix a security hole in a previously released assembly.

the fourth of these is considered the build number and assemblies that differ only by build number are intended to represent a recompilation from the same source. This would be appropriate, for example,because of processor, platform, or compiler changes.

6.2.2         Manifest Resources

A manifest resource is simply a named item of data associated with an assembly. A manifest resource is introduced using the .mresource directive, which adds the manifest resource to the assembly manifest begun by a preceding .assembly declaration.

<decl> ::=

Section

  .mresource [public | private] <dottedname>

               { <manResDecl>* }

 

| …

5.10

 

If the manifest resource is declared public it is exported from the assembly. If it is declared private it is not exported and hence only available from within the assembly. The <dottedname> is the name of the resource, and the optional quoted string is a description of the resource.

<manResDecl> ::=

Description

Section

  .assembly extern <dottedname>

Manifest resource is in external assembly with name <dottedname>.

6.3

| .custom <customDecl>

Custom attribute.

20

| .file <dottedname> at <int32>

Manifest resource is in file <dottedname> at byte offset <int32>.

 

 

For a resource stored in a file that is not a module (for example, an attached text file), the file shall be declared in the manifest using a separate (top-level) .file declaration (see clause 6.2.3) and the byte offset shall be zero  Similarly, a resource that is defined in another assembly is referenced using .assembly extern which requires that the assembly has been defined in a separate (top-level) .assembly extern directive (see Section 6.3).

6.2.3         Files in the Assembly

Assemblies may be associated with other files, e.g. documentation and other files that are used during execution. The declaration .file is used to add a reference to such a file to the manifest of the assembly:  (See Section 21.19)

<decl> ::=

Section

  .file [nometadata] <filename> .hash = ( <bytes> ) [.entrypoint]

 

| …

5.10

 

The attribute nometadata is specified if the file is not a module according to this specification.  Files that are marked as nometadata may have any format; they are considered pure data files.

The <bytes> after the .hash specify a hash value computed for the file. The VES shall recompute this hash value prior to accessing this file and shall generate an exception if it does not match. The algorithm used to calculate this hash value is specified with .hash algorithm (see clause 6.2.1.1).

If specified, the .entrypoint directive indicates that the entrypoint of a multi-module assembly is contained in this file.

Implementation Specific (Microsoft)

If the hash value is not specified, it will be automatically computed by the assembly linker al when an assembly file is created using al. Even though the hash value is optional in the grammar for ilasm, it is required at runtime.

6.3         Referencing Assemblies

<asmRefDecl> ::= .assembly extern <dottedname> [ as <dottedname> ]
                 { <asmRefDecl>* }

 

An assembly mediates all accesses from the files that it contains to other assemblies.  This is done through the metadata by requiring that the manifest for the executing assembly contain a declaration for any assembly referenced by the executing code.  The syntax .assembly extern as a top-level declaration is used for this purpose.  The optional as clause provides an alias which allows ilasm  to address external assemblies that have the same name, but differing in version, culture, etc.

The dotted name used in .assembly extern shall exactly match the name of the assembly as declared with .assembly directive in a case sensitive manner.  (So, even though an assembly might be stored within a file, within a filesystem that is case-blind, the names stored internally within metadata are case-sensitive, and shall match exactly.)

Implementation Specific (Microsoft)

The assembly mscorlib contains many of the types and methods in the Base Class Library.  For convenience, ilasm automatically inserts a .assembly extern mscorlib declaration if required

<asmRefDecl> ::=

Description

Section

  .hash = ( <bytes> )

Hash of referenced assembly

6.2.3

| .custom <customDecl>

Custom attributes

20

| .culture <QSTRING>

Culture of the referenced assembly

6.2.1.2

| .publickeytoken = ( <bytes> )

The low 8 bytes of the SHA1 hash of the originator's public key.

6.3

| .publickey = ( <bytes> )

The originator’s full public key

6.2.1.3

| .ver <int32> : <int32> : <int32> : <int32>

Major version, minor version, revision, and build

6.2.1.4

 

These declarations are the same as those for .assembly declarations (clause 6.2.1), except for the addition of .publickeytokenThis declaration is used to store the low 8 bytes of the SHA1 hash of the originator’s public key in the assembly reference, rather than the full public key.

An assembly reference can store either a full public key or an 8 byte “publickeytoken.” Either can be used to validate that the same private key used to sign the assembly at compile time signed the assembly used at runtime. Neither is required to be present, and while both can be stored this is not useful.

A conforming implementation of the CLI need not perform this validation, but it is permitted to do so, and it may refuse to load an assembly for which the validation fails.  A conforming implementation of the CLI may also refuse to permit access to an assembly unless the assembly reference contains either the public key or the public key token.  A conforming implementation of the CLI shall make the same access decision independent of whether a public key or a token is used.

Rationale: The full public key is cryptographically safer, but requires more storage space in the assembly reference.

Example (informative):

.assembly extern MyComponents

{ .publickey = (BB AA BB EE 11 22 33 00)

  .hash = (2A 71 E9 47 F5 15 E6 07 35 E4 CB E3 B4 A1 D3 7F 7F A0 9C 24)

  .ver 2:10:2002:0

}

6.4         Declaring Modules

All CIL files are modules and are referenced by a logical name carried in the metadata rather than their file name.  See Section 21.16.

<decl> ::=

Section

| .module <filename>

 

| …

5.10

 

Example (informative):

.module CountDown.exe

Implementation Specific (Microsoft)

If the .module directive is missing, ilasm will automatically add a .module directive and set the module name to be the file name, including its extension in capital letters. e.g., if the file is called foo and compiled into an exe, the module name will become “Foo.EXE”.  

Note that ilasm also generates a required GUID to uniquely identify this instance of the module and emits that into the Mvid metadata field: see clause 21.27.

6.5         Referencing Modules

When an item is in the current assembly but part of a different module than the one containing the manifest, the defining module shall be declared in the manifest of the assembly using the .module extern directive.  The name used in the .module extern directive of the referencing assembly shall exactly match the name used in the .module directive (see Section 6.4) of the defining module.  See Section 21.28.

<decl> ::=

Section

| .module extern <filename>

 

| …

5.10

 

Example (informative):

.module extern Counter.dll

6.6         Declarations inside a Module or Assembly

Declarations inside a module or assembly are specified by the following grammar. More information on each option can be found in the corresponding section.

<decl> ::=

Section

| .class <classHead> { <classMember>* }

9

| .custom <customDecl>

20

| .data <datadecl>

15.3.1

| .field <fieldDecl>

15

| .method <methodHead> { <methodBodyItem>* }

14

| <externSourceDecl>

5.7

| <securityDecl>

18

| …

 

 

6.7         Exported Type Definitions

The manifest module, of which there can only be one per assembly, includes the .assembly statement.  To export a type defined in any other module of an assembly requires an entry in the assembly’s manifest.  The following grammar is used to construct such an entry in the manifest:

<decl> ::=

Section

  .class extern <exportAttr> <dottedname> { <externClassDecl>* }

 

 

<externClassDecl> ::=

Section

  .file <dottedname>

| .class extern <dottedname>

| .custom <customDecl>

 

 

20

 

The <exportAttr> value shall be either public or nested public and shall match the visibility of the type.

For example, suppose an assembly consists of two modules A.EXE and B.DLL.  A.EXE contains the manifest.  A public class “Foo” is defined in B.DLL.  In order to export it – that is, to  make it visible by, and usable from, other assemblies –a .class extern statement shall be included in A.EXE.

Conversely, a public class “Bar” defined in A.EXE does not need any .class extern statement.

Rationale: Tools should be able to retrieve a single module, the manifest module, to determine the complete set types defined by the assembly.  Therefore, information from other modules within the assembly is replicated in the manifest module.  By convention, the manifest module is also known as the assembly.

7         Types and Signatures

The metadata provides mechanisms to both define types and reference types. Chapter 9 describes the metadata associated with a type definition, regardless of whether the type is an interface, class or a value type.

The mechanism used to reference types is divided into two parts. The first is the creation of a logical description of user-defined types that are referenced but (typically) not defined in the current module.  These are stored in a logical table in the metadata (see Section 21.35).

The second is a signature that encodes one or more type references, along with a variety of modifiers.  The grammar non-terminal <type> describes an individual entry in a signature.  The encoding of a signature is specified in Section 22.1.15.n cn

7.1         Types

The following grammar completely specifies all built-in types including pointer types of the CLI system. It also shows the syntax for user defined types that can be defined in the CLI system:

<type> ::=

Description

Section

  bool

Boolean

7.2

| boxed <typeReference>

Boxed user-defined value type

 

| char

16-bit Unicode code point

7.2

| class <typeReference>

User defined reference type.

7.3

| float32

32-bit floating point number

7.2

| float64

64-bit floating point number

7.2

| int8

Signed 8-bit integer

7.2

| int16

Signed 16-bit integer

7.2

| int32

Signed 32-bit integer

7.2

| int64

Signed 64-bit integer

7.2

| method <callConv> <type> *

         ( <parameters> )

Method pointer

13.5

| native int

Signed integer whose size varies depending on platform (32- or 64-bit)

7.2

| native unsigned int

Unsigned integer whose size varies depending on platform (32- or 64-bit)

7.2

| object

See System.Object in Partition IV_alink=Partition_IV

 

| string

See System.String in Partition IV_alink=Partition_IV

 

| <type> &

Managed pointer to <type>. <type> shall not be a managed pointer type or typedref

13.4

| <type> *

Unmanaged pointer to <type>

13.4

| <type> [ [<bound> [,<bound>]*] ]

Array of <type> with optional rank (number of dimensions) and bounds.

13.1and 13.2

| <type> modopt ( <typeReference> )

Custom modifier that may be ignored by the caller.

7.1.1

| <type> modreq ( <typeReference> )

Custom modifier that the caller shall understand.

7.1.1

| <type> pinned

For local variables only. The garbage collector shall not move the referenced value.

7.1.2

| typedref

Typed reference, created by mkrefany and used by refanytype or refanyval.

7.2

| valuetype <typeReference>

User defined value type (unboxed)

12

| unsigned int8

Unsigned 8-bit integers

7.2

| unsigned int16

Unsigned 16-bit integers

7.2

| unsigned int32

Unsigned 32-bit integers

7.2

| unsigned int64

Unsigned 64-bit integers

7.2

| void

No type.  Only allowed as a return type or as part of void *

7.2

 

In several situations the grammar permits the use of a slightly simpler mechanism for specifying types, by just allowing type names (e.g. “System.GC”) to be used instead of the full algebra (e.g. “class System.GC”).  These are called type specifications:

<typeSpec> ::=

Section

  [ [.module] <dottedname> ]

7.3

| <typeReference>

7.2

| <type>

7.1

 

7.1.1         modreq and modopt

Custom modifiers, defined using modreq (“required modifier”) and modopt (“optional modifier”), are similar to custom attributes (see Chapter 20) except that modifiers are part of a signature rather than attached to a declaration.  Each modifer associates a type reference with an item in the signature.

The CLI itself shall treat required and optional modifiers in the same manner. Two signatures that differ only by the addition of a custom modifier (required or optional) shall not be considered to match.  Custom modifiers have no other effect on the operation of the VES.

Rationale: The distinction between required and optional modifiers is important to tools other than the CLI that deal with the metadata, typically compilers and program analysers.  A required modifier indicates that there is a special semantics to the modified item that should not be ignored, while an optional modifier can simply be ignored. 

For example, the concept of const in the C programming language can be modelled with an optional modifier since the caller of a method that has a constant parameter need not treat it in any special way.  On the other hand, a parameter that shall be copy constructed in C++ shall be marked with a required custom attribute since it is the caller who makes the copy.

7.1.2         pinned

The signature encoding for pinned shall appear only in signatures that describe local variables (see clause 14.4.1.3). While a method with a pinned local variable is executing the VES shall not relocate the object to which the local refers.  That is, if the implementation of the CLI uses a garbage collector that moves objects, the collector shall not move objects that are referenced by an active pinned local variable.

Rationale: If unmanaged pointers are used to dereference managed objects, these objects shall be pinned.  This happens, for example, when a managed object is passed to a method designed to operate with unmanaged data.

7.2         Built-in Types

The CLI built-in types have corresponding value types defined in the Base Class Library. They shall be referenced in signatures only using their special encodings (i.e. not using the general purpose valuetype <typeReference> syntax).  Partition I_alink=Partition_I specifies the built-in types.

7.3         References to User-defined Types (<typeReference>)

User-defined types are referenced either using their full name and a resolution scope or (if one is available in the same module) a type definition (see Chapter 9).

A <typeReference> is used to capture the full name and resolution scope. 

<typeReference> ::=

  [<resolutionScope>] <dottedname> [/ <dottedname>]*

 

<resolutionScope> ::=

  [ .module <filename> ]

| [ <assemblyRefName> ]

 

<assemblyRefName> ::=

Section

  <dottedname>

5.1

 

The following resolution scopes are specified for un-nested types:

·              Current module (and, hence, assembly).  This is the most common case and is the default if no resolution scope is specified.  The type shall be resolved to a definition only if the definition occurs in the same module as the reference. 

Note: A type reference that refers to a type in the same module and assembly is better represented using a type definition.  Where this is not possible (for example, when referencing a nested type that has compilercontrolled accessibility) or convenient (for example, in some one-pass compilers) a type reference is equivalent and may be used.

·              Different module, current assembly.  The resolution scope shall be a module reference syntactically reprented using the notation [.module <filename>]. The type shall be resolved to a definition only if the referenced module (see Section 6.4) and type (see Section 6.7) have been declared by the current assembly and hence have entries in the assembly’s manifest.  Note that in this case the manifest is not physically stored with the referencing module.

·              Different assembly.  The resolution scope shall be an assembly reference syntactically represented using the notation [<assemblyRefName>]. The referenced assembly shall be declared in the manifest for the current assembly (see Section 6.3), the type shall be declared in the referenced assembly’s manifest, and the type shall be marked as exported from that assembly (see section 6.7 and clause 9.1.1).

·              For nested types, the resolution scope is always the enclosing type.  (See Section 9.6).  This is indicated syntactically by using a slash (“/”) to separate the enclosing type name from the nested type’s name

Example (informative):

The proper way to refer to a type defined in the base class library. The name of the type is System.Console and it is found in the assembly named mscorlib.

     .assembly extern mscorlib { }

     .class [mscorlib]System.Console

 

A reference to the type named C.D in the module named x in the current assembly.

     .module extern x

     .class [.module x]C.D

 

A reference to the type named C nested inside of the type named Foo.Bar in another assembly, named MyAssembly.

     .assembly extern MyAssembly { }

     .class [MyAssembly]Foo.Bar/C

7.4         Native Data Types

Some implementations of the CLI will be hosted on top of existing operating systems or runtime platforms that specify data types required to perform certain functions.  The metadata allows interaction with these native data types by specifying how the built-in and user-defined types of the CLI are to be marshalled to and from native data types.  This marshalling information can be specified (using the keyword marshal) for

·              the return type of a method, indicating that a native data type is actually returned and shall be marshalled back into the specified CLI data type

·              a parameter to a method, indicating that the CLI data type provided by the caller shall be marshalled into the specified native data type (if the parameter is passed by reference the updated value shall be marshalled back from the native data type into the CLI data type when the call is completed)

·              a field of a user-defined type, indicating that any attempt to pass the object in which it occurs to platform methods shall make a copy of the object, replacing the field by the specified native data type (if the object is passed by reference then the updated value shall be marshalled back when the call is completed)

The following table lists all native types supported by the CLI and provides a description for each of them.  A more complete description can be found in Partition IV_alink=Partition_IV in the definition of the enum System.Runtime.Interopservices.UnmanagedType, which provides the actual values used to encode the types.  All encoding values from 0 through 63 are reserved for backward compatibility with existing implementations of the CLI.  Values 64 through 127 are reserved for future use in this and related Standards.

<nativeType> ::=

Description

Name in
class library

 [ ]

Native array. Type and size are determined at runtime from the actual marshaled array.

LPArray

| bool

Boolean. 4-byte integer value where a non-zero value represents TRUE and 0 represents FALSE.

Bool

| float32

32-bit floating point number.

FLOAT32

| float64

64-bit floating point number.

FLOAT64

| [unsigned] int

Signed or unsigned integer, sized to hold a pointer on the platform

SysUInt or SysInt

| [unsigned] int8

Signed or unsigned 8-bit integer

unsigned int8 or int8

| [unsigned] int16

Signed or unsigned 16-bit integer

unsigned int16 or int16

| [unsigned] int32

Signed or unsigned 32-bit integer

unsigned int32 or int32

| [unsigned] int64

Signed or unsigned 64-bit integer

unsigned int64 or int64

| lpstr

A pointer to a null terminated array of ANSI characters. Code page is implementation specific.

LPStr

| lptstr

A pointer to a null terminated array of platform characters (ANSI or Unicode).  Code page and character encoding are implementation specific.

LPTStr

| lpvoid

An untyped pointer, platform specifies size.

LPVoid

| lpwstr

A pointer to a null terminated array of Unicode characters.  Character encoding is implementation specific.

LPWStr

| method

A function pointer.

FunctionPtr

| <nativeType> [ ]

Array of <nativeType>. The length is determined at runtime by the size of the actual marshaled array.

LPArray

| <nativeType> [ <int32> ]

Array of <nativeType> of length <int32>.

LPArray

| <nativeType>
[ + <int32> ]

Array of <nativeType> with runtime supplied element size. The int32 specifies a parameter to the current method (counting from parameter number 0) that, at runtime, will contain the size of an element of the array in bytes.  Can only be applied to methods, not fields.

LPArray

| <nativeType>
[ <int32> + <int32> ]

Array of <nativeType> with runtime supplied element size. The first int32 specifies the number of elements in the array.  The second int32 specifies which parameter to the current method (counting from parameter number 1) will specify the additional number of elements in the array.   Can only be applied to methods, not fields

LPArray

 

Implementation Specific (Microsoft)

The Microsoft implementation supports a richer set of types to describe marshalling between Windows native types and COM.  These additional options are listed in the following table:

Implementation Specific (Microsoft)

<nativeType> ::=

Description

Name in
class library

| as any

Determines the type of an object at runtime and marshals the Object as that type.

AsAny

| byvalstr

A string in a fixed length buffer.

VBByRefStr

| custom ( <QSTRING>,
  <QSTRING> )

Custom marshaler.  The 1st string is the name of the marshalling class, using the string conventions of Reflection.Emit to specify the assembly and/or module.  The 2nd is an arbitrary string passed to the marshaller at runtime to identify the form of marshalling required.

CustomMarshaler

| fixed array [ <int32> ]

A fixed size array of length <int32> bytes

ByValArray

| fixed sysstring
[ <int32> ]

A fixed size system string of length <int32>.  This can only be applied to fields, and a separate attribute specifies the encoding of the string.

ByValTStr

| lpstruct

A pointer to a C-style structure. Used to marshal managed formatted types.

LPStruct

| struct

A C-style structure, used to marshal managed formatted types.

Struct

 

Example (informative):

.method int32 M1( int32 marshal(int32), bool[] marshal(bool[5]) )

 

Method M1 takes two arguments: an int32, and an array of 5 bools

 

++++++++++

 

.method int32 M2( int32 marshal(int32), bool[] marshal(bool[+1]) )

 

Method M2 takes two arguments: an int32, and an array of bools: the number of elements in that array is given by the value of the first parameter

 

++++++++++

 

.method int32 M3( int32 marshal(int32), bool[] marshal(bool[7+1]) )

 

Method M3 takes two arguments: an int32, and an array of bools: the number of elements in that array is given as 7 plus the value of the first parameter

 

8         Visibility, Accessibility and Hiding

Partition I_alink=Partition_I specifies visibility and accessibility. In addition to these attributes, the metadata stores information about method name hiding. Hiding controls which method names inherited from a base type are available for compile-time name binding.

8.1         Visibility of Top-Level Types and Accessibility of Nested Types

Visibility is attached only to top-level types, and there are only two possibilities: visible to types within the same assembly, or visible to types regardless of assembly. For nested types (i.e. types that are members of another type) the nested type has an accessibility that further refines the set of methods that can reference the type. A nested type may have any of the 7 accessibility modes (see Partition I_alink=Partition_I), but has no direct visibility attribute of its own, using the visibility of its enclosing type instead.

Because the visibility of a top-level type controls the visibility of the names of all of its members, a nested type cannot be more visible than the type in which it is nested. That is, if the enclosing type is visible only within an assembly then a nested type with public accessibility is still only available within the assembly. By contrast, a nested type that has assembly accessibility is restricted to use within the assembly even if the enclosing type is visible outside the assembly.

To make the encoding of all types consistent and compact, the visibility of a top-level type and the accessibility of a nested type are encoded using the same mechanism in the logical model of clause 22.1.14.

8.2         Accessibility

Accessibility is encoded directly in the metadata.  See, for example, clause 21.24.

8.3         Hiding

Hiding is a compile-time concept that applies to individual methods of a type. The CTS specifies two mechanisms for hiding, specified by a single bit:

·              hide-by-name, meaning that the introduction of a name in a given type hides all inherited members of the same kind (method or field) with the same name.

·              hide-by-name-and-sig, meaning that the introduction of a name in a given type hides any inherited member of the same kind but with precisely the same type (for fields) or signature (for methods, properties, and events).

There is no runtime support for hiding.  A conforming implementation of the CLI treats all references as though the names were marked hide-by-name-and-sigCompilers that desire the effect of hide-by-name can do so by marking method definitions with the newslot attribute (see clause 14.4.2.3) and correctly chosing the type used to resolve a method reference  (see clause 14.1.3).

9         Defining Types

Types (i.e., classes, value types, and interfaces) may be defined at the top-level of a module:

<decl> ::=

Section

  .class <classHead> { <classMember>* }

9

| …

 

 

The logical metadata table created by this declaration is specified in Section 21.34.

Rationale: For historical reasons, many of the syntactic classes used for defining types incorrectly use “class” instead of “type” in their name.  All classes are types, but “types” is a broader term encompassing value types, and interfaces.

9.1         Type Header (<classHead>)

A type header consists of

·              any number of type attributes

·              a name (an <id>)

·              a base type (or parent type), which defaults to [mscorlib]System.Object

·              an optional list of interfaces whose contract this type and all its descendent types shall satisfy

<classHead> ::=

  <classAttr>* <id> [extends <typeReference>] [implements <typeReference> [, <typeReference>]*]

 

The extends keyword defines the base type of a type. A type shall extend from exactly one other type. If no type is specified, ilasm will add an extend clause to make the type inherit from System.Object.

The implements keyword defines the interfaces of a type. By listing an interface here, a type declares that all of its concrete implementations will support the contract of that interface, including providing implementations of any virtual methods the interface declares.  See also Chapter 10 and Chapter 11.

Example (informative):

.class private auto autochar CounterTextBox

   extends [System.Windows.Forms]System.Windows.Forms.TextBox

   implements [.module Counter]CountDisplay

{ // body of the class

}

This code declares the class CounterTextBox, which extends the class System.Windows.Forms.TextBox in the assembly System.Windows.Forms and implements the interface CountDisplay in the module Counter of the current assembly. The attributes private, auto and autochar are described in the following sections.

A type can have any number of custom attributes attached.  Custom attributes are attached as described in Chapter 20. The other (predefined) attributes of a type may be grouped into attributes that specify visibility, type layout information, type semantics information, inheritance rules, interoperation information, and information on special handling. The following subsections provide additional information on each group of predefined attributes.

<classAttr> ::=

Description

Section

  abstract

Type is abstract.

9.1.4

| ansi

Marshal strings to platform as ANSI.

9.1.5

| auto

Auto layout of type.

9.1.2

| autochar

Marshal strings to platform based on platform.

9.1.5

| beforefieldinit

Calling static methods  does not initialize type.

9.1.6

| explicit

Layout of fields is provided explicitly.

9.1.2

| interface

Interface declaration.

9.1.3

| nested assembly

Assembly accessibility for nested type.

9.1.1

| nested famandassem

Family and Assembly accessibility for nested type.

9.1.1

| nested family

Family accessibility for nested type.

9.1.1

| nested famorassem

Family or Assembly accessibility for nested type.

9.1.1

| nested private

Private accessibility for nested type.

9.1.1

| nested public

Public accessibility for nested type.

9.1.1

| private

Private visibility of top-level type.

9.1.1

| public

Public visibility of top-level type.

9.1.1

| rtspecialname

Special treatment by runtime.

9.1.6

| sealed

The type cannot be subclassed.

9.1.4

| sequential

The type is laid out sequentially.

9.1.2

| serializable

Type may be serialized.

9.1.6

| specialname

Special treatment by tools.

9.1.6

| unicode

Marshal strings to platform as Unicode.

9.1.5

 

Implementation Specific (Microsoft)

The above grammar also includes

<classAttr> ::= import

to indicate that the type is imported from a COM type library

9.1.1         Visibility and Accessibility Attributes

<classAttr> ::= …

| nested assembly

| nested famandassem

| nested family

| nested famorassem

| nested private

| nested public

| private

| public

 

See Partition I_alink=Partition_I.  A type that is not nested inside another shall have exactly one visibility (private or public) and shall not have an accessiblity.  Nested types shall have no visibility, but instead shall have exactly one of the accessibility attributes (nested assembly, nested famandassem, nested family, nested famorassem, nested private, or nested public). The default visibility for top-level types is private. The default accessibility for nested types is nested private.

9.1.2         Type Layout Attributes

<classAttr> ::= …

| auto

| explicit

| sequential

 

The type layout specifies how the fields of an instance of a type are arranged. A given type shall have only one layout attribute specified.  By convention, ilasm supplies auto if no layout attribute is specified.

auto: the layout shall be done by the CLI, with no user-supplied constraints

explicit: the layout of the fields is explicitly provided (see Section 9.7).

sequential: the CLI shall lay out the fields in sequential order, based on the order of the fields in the logical metadata table (see Section 21.15).

Rationale: The default auto layout should provide the best layout for the platform on which the code is executing.  sequential layout is intended to instruct the CLI to match layout rules commonly followed by languages like C and C++ on an individual platform, where this is possible while still guaranteeing verifiable layout.  explicit layout allows the CIL generator to specify the precise layout semantics; specific rules govern which explicit layouts are verifiable.

9.1.3         Type Semantics Attributes

<classAttr> ::= …

| interface

 

The type semantic attributes specify whether an interface, class, or value type shall be defined.  The interface attribute specifies an interface.  If this attribute is not present and the definition extends (directly or indirectly) System.ValueType a value type shall be defined (see Chapter 12).   Otherwise, a class shall be defined (see Chapter 10).

Note that the runtime size of a value type shall not exceed 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

The current implementation allows 0x3F0000 bytes, but may be reduced in future

9.1.4         Inheritance Attributes

<classAttr> ::= …

| abstract

| sealed

 

Attributes that specify special semantics are abstract and sealed. These attributes may be used together.

abstract specifies that this type shall not be instantiated.  If a type contains abstract methods, the type shall be declared as an abstract type.

sealed specifies that a type shall not have subclasses.  All value types shall be sealed.

Rationale: Virtual methods of sealed types are effectively instance methods, since they cannot be overridden. Framework authors should use sealed classes sparingly since they do not provide a convenient building block for user extensibility.  Sealed classes may be necessary when the implementation of a set of virtual methods for a single class (typically inherited from different interfaces) becomes interdependent or depends critically on implementation details not visible to potential subclasses.

A type that is both abstract and sealed should have only static members, and serves as what some languages call a namespace.

9.1.5         Interoperation Attributes

<classAttr> ::= …

| ansi

| autochar

| unicode

 

These attributes are for interoperation with unmanaged code.  They specify the default behavior to be used when calling a method (static, instance, or virtual) on the class that has an argument or return type of System.String and does not itself specify marshalling behavior.  Only one value shall be specified for any type, and the default value is ansi.

ansi specifies that marshalling shall be to and from ANSI strings

unicode specifies that marshalling shall be to and from Unicode strings

autochar specifies either ANSI or Unicode behavior, depending on the platform on which the CLI is running.

9.1.6         Special Handling Attributes

<classAttr> ::= …

| beforefieldinit

| serializable

| specialname

| rtspecialname

 

These attributes may be combined in any way.

beforefieldinit instructs the CLI that it need not initialize the type before a static method is called.  See clause 9.5.3.

Implementation Specific (Microsoft)

serializable indicates that the fields of the type may be serialized into a data stream by the CLI serializer.  See Partition IV_alink=Partition_IV.

specialname indicates that the name of this item may have special significance to tools other than the CLI.  See, for example, Partition I_alink=Partition_I .

rtspecialname indicates that the name of this item has special significance to the CLI.  There are no currently defined special type names; this is for future use.  Any item marked rtspecialname shall also be marked specialname

Rationale: If an item is treated specially by the CLI, then tools should also be made aware of that. The converse is not true.

9.2         Body of a Type Definition

A type may contain any number of further declarations. The directives .event, .field, .method, and .property are used to declare members of a type. The directive .class inside a type declaration is used to create a nested type, which is discussed in further detail in Section 9.6.

<classMember> ::=

Description

Section

  .class <classHead> { <classMember>* }

Defines a nested type.

9.6

| .custom <customDecl>

Custom attribute.

20

| .data <datadecl>

Defines static data associated with the type.

15.3

| .event <eventHead> { <eventMember>* }

Declares an event.

17

| .field <fieldDecl>

Declares a field belonging to the type.

15

| .method <methodHead> { <methodBodyItem>* }

Declares a method of the type.

14

| .override <typeSpec> :: <methodName> with <callConv> <type> <typeSpec> :: <methodName> ( <parameters> )

Specifies that the first method is overridden by the definition of the second method.

9.3.2

| .pack <int32>

Used for explicit layout of fields.

9.7

| .property <propHead> { <propMember>* }

Declares a property of the type.

16

| .size <int32>

Used for explicit layout of fields.

9.7

| <externSourceDecl>

.line

5.7

| <securityDecl>

.permission or .capability

19

 

9.3         Introducing and Overriding Virtual Methods

A virtual method of a base type is overridden by providing a direct implementation of the method (using a method definition, see Section 14.4) and not specifying it to be newslot (see clause 14.4.2.3).  An existing method body may also be used to implement a given virtual declaration using the .override directive (see clause 9.3.2).

9.3.1         Introducing a Virtual Method

A virtual method is introduced in the inheritance hierarchy by defining a virtual method (see Section 14.4). The versioning semantics differ depending on whether or not the definition is marked as newslot (see clause 14.4.2.3):

If the definition is marked newslot then the definition always creates a new virtual method, even if a base class provides a matching virtual method.  Any reference to the virtual method created before the new virtual function was defined will continue to refer to the original definition.

If the definition is not marked newslot then it creates a new virtual method only if there is no virtual method of the same name and signature inherited from a base class.  If the inheritance hierarchy changes so that the definition matches an inherited virtual function the definition will be treated as a new implementation of the inherited function.

9.3.2         The .override DirectiveUsusally the VES

The .override directive specifies that a virtual method should be implemented (overridden), in this type, by a virtual method with a different name but with the same signature. It can be used to provide an implementation for a virtual method inherited from a base class or a virtual method specified in an interface implemented by this type.  The .override directive specifies a Method Implementation (MethodImpl) in the metadata (see clause 14.1.4).

<classMember> ::=

Section

  .override <typeSpec> :: <methodName> with <callConv> <type> <typeSpec> :: <methodName> ( <parameters> )

 

| …

9.2

 

The first <typeSpec> :: <methodName> pair specifies the virtual method that is being overridden.  It shall reference either an inherited virtual method or a virtual method on an interface that the current type implements.  The remaining information specifies the virtual method that provides the implementation. 

While the syntax specified here and the actual metadata format (see Section 21.25 )allows any virtual method to be used to provide an implementation, a conforming program shall provide a virtual method actually implemented directly on the type containing the .override directive.

Rationale: The metadata is designed to be more expressive than can be expected of all implementations of the VES.

Example (informative):

The following example shows a typical use of the .override directive. A method implementation is provided for a method declared in an interface (see Chapter 11).

.class interface I

{ .method public virtual abstract void m() cil managed {}

}

.class C implements I

{ .method virtual public void m2()

  { // body of m2

  }

  .override I::m with instance void C::m2()

}

The .override directive specifies that the C::m2 body shall provide the implementation of be used to implement I::m on objects of class C.

9.3.3         Accessibility and Overriding

If a type overrides an inherited method, it may widen, but it shall not narrow, the accessibility of that method.  As a principle, if a client of a type is allowed to access a method of that type, then it should also be able to access that method (identified by name and signature) in any derived type.  Table 7.1 specifies narrow and widen in this contexta “Yes” denotes that the subclass can apply that accessibility, a “No” denotes it is illegal.

Table 7.1: Legal Widening of Access to a Virtual Method

Subclass

Base type Accessibility

 

private

family

assembly

famandassem

famorassem

public

private

Yes

No

No

No

No

No

family

Yes

Yes

No

No

If not in same assembly

No

assembly

Yes

No

Same assembly

No

No

No

famandassem

Yes

No

No

Same assembly

No

No

famorassem

Yes

Yes

Same assembly

Yes

Same assembly

No

public

Yes

Yes

Yes

Yes

Yes

Yes

 

Note: A method may be overridden even if it may not be accessed by the subclass.

If a method has assembly accessibility, then it shall have public accessibility if it is being overridden by a method in a different assembly. A similar rule applies to famandassem, where also famorassem is allowed outside the assembly. In both cases assembly or famandassem, respectively, may be used inside the same assembly.

A special rule applies to famorassem, as shown in the table. This is the only case where the accessibility is apparently narrowed by the subclass. A famorassem method may be overridden with family accessibility by a type in another assembly.

Rationale: Because there is no way to specify “family or specific other assembly” it  is not possible to specify that the accessibility should be unchanged.  To avoid narrowing access, it would be necessary to specify an accessibility of public, which would force widening of access even when it  is not desired.  As a compromise, the minor narrowing of “family” alone is permitted.

9.4         Method Implementation Requirements

A type (concrete or abstract) may provide

·              implementations for instance, static, and virtual methods that it introduces

·              implementations for methods declared in interfaces that it has specified it will implement, or that its base type  has specified it will implement

·              alternative implementations for virtual methods inherited from its parent

·              implementations for virtual methods inherited from an abstract base type that did not provide an implementation

A concrete (i.e. non-abstract) type shall provide either directly or by inheritance an implementation for

·              all methods declared by the type itself

·              all virtual methods of interfaces implemented by the type

·              all virtual methods that the type inherits from its base type

9.5         Special Members

There are three special members, all methods, that can be defined as part of a type: instance constructors, instance finalizers, and type initializers.

9.5.1         Instance constructors

Instance constructors initialize an instance of a type. An instance constructor is called when an instance of a type is created by the newobj instruction (see Partition III_alink=Partition_III).  Instance constructors shall be instance (not static or virtual) methods, they shall be named .ctor and marked both rtspecialname and specialname (see clause 14.4.2.6). Instance constructors may take parameters, but shall not return a value. Instance constructors may be overloaded (i.e. a type may have several instance constructors). Each instance constructor shall have a unique signature. Unlike other methods, instance constructors may write into fields of the type that are marked with the initonly attribute (see clause 15.1.2).

Example (informative):

The following shows the definition of an instance constructor that does not take any parameters:

.class X {

.method public rtspecialname specialname instance void .ctor() cil managed

{ .maxstack 1

// call super constructor

ldarg.0              // load this pointer

call instance void [mscorlib]System.Object::.ctor()

// do other initialization work

ret

}

}

9.5.2         Instance Finalizer

The behavior of finalizers is specified in Partition I_alink=Partition_I. The finalize method for a particular type is specified by overriding the virtual method Finalize in System.Object.

9.5.3         Type Initializer

Types may contain special methods called type initializers to initialize the type itself.

All types (classes, interfaces, and value types) may have a type initializer.  This method shall be static, take no parameters, return no value, be marked with rtspecialname and specialname (see clause 14.4.2.6), and be named .cctor.

Like instance initializers, type initializers may write into static fields of their type that are marked with the initonly attribute (see clause 15.1.2).

Note: Type initializers are often simple methods that initialize the type’s static fields from stored constants or via simple computations. There are, however, no limitations on what code is permitted in a type initializer.

9.5.3.1          Type Initialization Guarantees

The CLI shall provide the following guarantees regarding type initialization (but see also clause 9.5.3.2 and clause 9.5.3.3):

1.             When type initializers are executed is specified in Partition I_alink=Partition_I

2.             A type initializer shall run exactly once for any given type, unless explicitly called by user code

3.             No method other than those called directly or indirectly from the type initializer will be able to access members of a type before its initializer completes execution.

9.5.3.2          Relaxed Guarantees

A type can be marked with the attribute beforefieldinit (see clause 9.1.6) to indicate that all the guarantees specified in clause 9.5.3.1  are not required.  In particular, the final requirement of guarantee 1 need not be provided: the type initializer  need not run before a static method is called or referenced.

Rationale: When code can be executed in multiple application domains it becomes particularly expensive to ensure this final guarantee.  At the same time, examination of large bodies of managed code have shown that this final guarantee is rarely required, since type initializers are almost always simple methods for initializing static fields.  Leaving it up to the CIL generator (and hence, possibly, to the programmer) to decide whether this guarantee is required therefore provides efficiency when it is desired at the cost of consistency guarantees.

9.5.3.3          Races and Deadlocks

In addition to the type initialization guarantees specified in clause 9.5.3.1 the CLI shall ensure two further guarantees for code that is called from a type initializer:

1.             Static variables of a type are in a known state prior to any access whatsoever.

2.             Type initialization alone shall not create a deadlock unless some code called from a type initializer (directly or indirectly) explicitly invokes blocking operations.

Rationale:

Consider the following two class definitions:

.class public A extends [mscorlib]System.Object

{ .field static public class A a

  .field static public class B b

 

  .method public static rtspecialname specialname void .cctor ()

  { ldnull                    // b=null

    stsfld class B A::b

    ldsfld class A B::a       // a=B.a

    stsfld class A A::a

    ret

  }

}

 

.class public B extends [mscorlib]System.Object

{ .field static public class A a

  .field static public class B b

 

  .method public static rtspecialname specialname void .cctor ()

  { ldnull                    // a=null

    stsfld class A B::a

    ldsfld class B A::b       // b=A.b

    stfld class B B::b

    ret

  }

}

After loading these two classes, an attempt to reference any of the static fields causes a problem, since the type initializer for each of A and B requires that the type initializer of the other be invoked first. Requiring that no access to a type be permitted until its initializer has  completed would create a deadlock situation. Instead, the CLI provides a weaker guarantee: the initializer will have started to run, but it need not have completed. But this alone would allow the full uninitialized state of a type to be visible, which would make it difficult to guarantee repeatable results.

There are similar, but more complex, problems when type initialization takes place in a multi-threaded system. In these cases, for example, two separate threads might start attempting to access static variables of separate types (A and B) and then each would have to wait for the other to complete initialization.

A rough outline of the algorithm is as follows:

1. At class load time (hence prior to initialization time) store zero or null into all static fields of the type.

2. If the type is initialized you are done.

2.1. If the type is not yet initialized, try to take an initialization lock. 

2.2. If successful, record this thread as responsible for initializing the type and proceed to step 2.3.

2.2.1. If not, see whether this thread or any thread waiting for this thread to complete already holds the lock.

2.2.2. If so, return since blocking would create a deadlock.  This thread will now see an incompletely initialized state for the type, but no deadlock will arise.

2.2.3  If not, block until the type is initialized then return.

2.3 Initialize the parent type and then all interfaces implemented by this type.

2.4 Execute the type initialization code for this type.

2.5 Mark the type as initialized, release the initialization lock, awaken any threads waiting for this type to be initialized, and return.

9.6         Nested Types

Nested types are specified in Partition I_alink=Partition_I. Interfaces may be nested inside of classes and value types, but classes and value types shall not be nested inside of interfaces. For information about the logical tables associated with nested types, see Section 21.29.

Note: A nested type is not associated with an instance of its enclosing type. The nested type has its own base type and may be instantiated independent of the enclosing type. This means that the instance members of the enclosing type are not accessible using the this pointer of the nested type.

A nested type may access any members of its enclosing type, including private members, as long as the member is static or the nested type has a reference to an instance of the enclosing type. Thus, by using nested types a type may give access to its private members to another type.

On the other side, the enclosing type may not access any private or family members of the nested type. Only members with assembly, famorassem, or public accessibility can be accessed by the enclosing type.

Example (informative):

The following example shows a class declared inside another class. Both classes declare a field. The nested class may access both fields, while the enclosing class does not have access to the field b.

.class private auto autochar CounterTextBox

       extends [System.Windows.Forms]System.Windows.Forms.TextBox

implements [.module Counter]IcountDisplay

{ .field static private int32 a

  /* Nested class. Declares the NegativeNumberException */

  .class nested assembly NonPositiveNumberException extends [mscorlib]System.Exception

  { .field static private int32 b

    // body of nested class

  } // end of nested class NegativeNumberException

}

9.7         Controlling Instance Layout

The CLI supports both sequential and explicit layout control, see clause 9.1.2. For explicit layout it is also necessary to specify the precise layout of an instance, see also Section 21.18 and Section 21.16.

<fieldDecl> ::=

  [[ <int32> ]] <fieldAttr>* <type> <id>

 

The optional int32 specified in brackets at the beginning of the declaration specifies the byte offset from the beginning of the instance of the type.  This form of explicit layout control shall not be used with global fields specified using the at notation (see clause 15.3.2).

Offset values shall be 0 or greater; they cannot be negative. It is possible to overlap fields in this way, even though it is not recommended. The field may be accessed using pointer arithmetic and ldind to load the field indirectly or stind to store the field indirectly (see Partition III_alink=Partition_III).  See Section 21.18 and Section 21.16 for encoding of this information. For explicit layout, every field shall be assigned an offset.

The .pack directive specifies that fields should be placed within the runtime object at addresses which are a multiple of the specified number, or at natural alignment for that field type, whichever is smaller.  e.g., .pack 2 would allow 32-bit-wide fields to be started on even addresses – whereas without any .pack directive, they would be naturally aligned – that is to say, placed on addresses that are a multiple of 4.  The integer following .pack shall be one of 0, 1, 2, 4, 8, 16, 32, 64 or 128.  (A value of zero indicates that the pack size used should match the default for the current platform).  The .pack directive shall not be supplied for any type with explicit layout control.

The directive .size specifies that a memory block of the specified amount of bytes shall be allocated for an instance of the type. e.g., .size 32 would create a block of 32 bytes for the instance.  The value specified shall be greater than or equal to the calculated size of the class, based upon its field sizes and any .pack directive.  Note that if this directive applies to a value type, then the size shall be less than 1 MByte.

Note:  Metadata that controls instance layout is not a “hint,” it is an integral part of the VES that shall be supported by all conforming implementations of the CLI.

Example (informative):

The following class uses sequential layout of its fields:

.class sequential public SequentialClass

{ .field public int32 a             // store at offset 0 bytes

  .field public int32 b             // store at offset 4 bytes

}

The following class uses explicit layout of its fields:

.class explicit public ExplicitClass

{ .field [0] public int32 a   // store at offset 0 bytes

  .field [6] public int32 b   // store at offset 6 bytes

}

The following value type uses .pack to pack its fields together:

.class value sealed public MyClass extends [mscorlib]System.ValueType

{ .pack 2

  .field  public int8  a      // store at offset 0 bytes

  .field  public int32 b      // store at offset 2 bytes (not 4)

}

The following class specifies a contiguous block of 16 bytes:

.class public BlobClass

{ .size  16

}

9.8         Global Fields and Methods

In addition to types with static members, many languages have the notion of data and methods that are not part of a type at all. These are referred to as global fields and methods.

It is simplest to understand global fields and methods in the CLI by imagining that they are simply members of an invisible abstract public class. In fact, the CLI defines such a special class, named ′<Module>′, that does not have a base type and does not implement any interfaces. The only noticeable difference is in how definitions of this special class are treated when multiple modules are combined together, as is done by a class loader. This process is known as metadata merging.

For an ordinary type, if the metadata merges two definitions of the same type, it simply discards one definition on the assumption they are equivalent and that any anomaly will be discovered when the type is used.  For the special class that holds global members, however, members are unioned across all modules at merge time. If the same name appears to be defined for cross-module use in multiple modules then there is an error.  In detail:

·              If no member of the same kind (field or method), name, and signature exists, then add this member to the output class.

·              If there are duplicates and no more than one has an accessibility other than compilercontrolled, then add them all in the output class.

·              If there are duplicates and two or more have an accessibility other than compilercontrolled an error has occurred.

10      Semantics of Classes

Classes, as specified in Partition I_alink=Partition_I, define types in an inheritance hierarchy.  A class (except for the built-in class System.Object) shall declare exactly one parent class.  A class shall declare zero or more interfaces that it implements (see Chapter 11).  A concrete class may be instantiated to create an object, but an abstract class (see clause 9.1.4) shall not be instantiated.   A class may define fields (static or instance), methods (static, instance, or virtual), events, properties, and nested types (classes, value types, or interfaces).

Instances of a class (objects) are created only by explicitly using the newobj instruction (see Partition III_alink=Partition_III).  When a variable or field that has a class as its type is created (for example, by calling a method that has a local variable of a class type) the value shall initially be null, a special value that is assignment compatible with all class types even though it is not an instance of any particular class.

11      Semantics of Interfaces

Interfaces, as specified in Partition I_alink=Partition_I, define a contract that other types may implement. Interfaces may have static fields and methods, but they shall not have instance fields or methods.  Interfaces may define virtual methods, but only if they are abstract (see Partition I_alink=Partition_I and clause 14.4.2.4).

Rationale: Interfaces cannot define instance fields for the same reason that the CLI does not support multiple inheritance of base types: in the presence of dynamic loading of data types there is no known implementation technique that is both efficient when used and has no cost when not used.  By contrast, providing static fields and methods need not affect the layout of instances and therefore does not raise these issues.

Interfaces may be nested inside any type (interface, class, or value type).  Classes and value types shall not be nested inside of interfaces.

11.1      Implementing Interfaces

Classes and value types shall implement zero or more interfaces.  Implementing an interface implies that all concrete instances of the class or value type shall provide an implementation for each abstract virtual method declared in the interface.   In order to implement an interface, a class or value type shall either explicitly declare that it does so (using the implements attribute in its type definition, see Section 9.1) or shall be derived from a base class that implements the interface.

Note: An abstract class (since it cannot be instantiated) need not provide implementations of the virtual methods of interfaces it implements, but any concrete class derived from it shall provide the implementation.

Merely providing implementations for all of the abstract methods of an interface is not sufficient to have a type implement that interface.  Conceptually, this represents that fact that an interface represents a contract that may have more requirements than are captured in the set of abstract methods.  From an implementation point of view, this allows the layout of types to be constrained only by those interfaces that are explicitly declared.

Interfaces shall declare that they require the implementation of zero or more other interfaces. If one interface, A, declares that it requires the implementation of another interface, B, then A implicitly declares that it requires the implementation of all interfaces required by B. If a class or value type declares that it implements A, then all concrete instances shall provide implementations of the virtual methods declared in A and all of the interfaces A requires.

Example (informative):

The following class implements the interface IStartStopEventSource defined in the module Counter.

.class private auto autochar StartStopButton
       extends [System.Windows.Forms]System.Windows.Forms.Button
       implements [.module Counter]IstartStopEventSource
{ // body of class
}

11.2      Implementing Virtual Methods on Interfaces

Classes that implement an interface (see Section 11.1) are required to provide implementations for the abstract virtual methods defined by the interface.  There are three mechanisms for providing this implementation:

·              directly specifying an implementation, using the same name and signature as appears in the interface

·              inheritance of an existing implementation from the base type

·              use of an explicit MethodImpl (see clause 14.1.4).

The Virtual Execution System shall determine the appropriate implementation of a virtual method to be used for an interface abstract method using the following algorithm. 

·              If the parent class implements the interface, start with the same virtual methods that it provides, otherwise create an interface that has empty slots for all virtual functions.

·              If this class explicitly specifies that it implements the interface

o             if the class defines any public virtual newslot functions whose name and signature match a virtual method on the  interface, then use these new virtual methods to implement the corresponding interface method.

·              If there are any virtual methods in the interface that still have empty slots, see if there are any public virtual methods available on this class (directly or inherited) and use these to implement the corresponding methods on the interface.

·              Apply all MethodImpls that are specified for this class, thereby placing explicitly specified virtual methods into the interface in preference to those inherited or chosen by name matching.

·              If the current class is not abstract and there are any interface methods that still have empty slots, then the program is not valid.

Rationale: Interfaces can be thought of as specifying, primarily, a set of virtual methods that shall be implemented by any class that implements the interface.  The class specifies a mapping from its own virtual methods to those of the interface.  Thus it is virtual methods, not specific implementations of those methods, that are associated with interfaces.  Overriding a virtual method on a class with a specific implementation will thus affect not only the virtual method named in the class but also any interface virtual methods to which that same virtual method has been mapped.

12      Semantics of Value Types

In contrast to classes, value types (see Partition I_alink=Partition_I) are not accessed by using a reference but are stored directly in the location of that type.

Rationale: Value types are used to describe the type of small data items. They can be compared to struct (as opposed to pointers to struct) types in C++. Compared to reference types, value types are accessed faster since there is no additional indirection involved. As elements of arrays they do not require allocating memory for the pointers as well as for the data itself.  Typical value types are complex numbers, geometric points, or dates.

Like other types, value types may have fields (static or instance), methods (static, instance, or virtual), properties, events, and nested types.  A value type may be converted into a corresponding reference type (its boxed form, a class automatically created for this purpose by the VES when a value type is defined) by a process called boxing. A boxed value type may be converted back into its value type representation, the unboxed form, by a process called unboxing.  Value types shall be sealed, and they shall have a base type of either System.ValueType or System.Enum (see Partition IV_alink=Partition_IV).  Value types shall implement zero or more interfaces, but this has meaning only in their boxed form (see Section 12.3).

Unboxed value types are not considered subtypes of another type and it is not valid to use the isinst instruction (see Partition III_alink=Partition_III) on unboxed value types. The isinst instruction may be used for boxed value types.  Unboxed value types shall not be assigned the value null and they shall not be compared to null.

Value types support layout control in the same way as reference types do (see Section 9.7). This is especially important when values are imported from native code.

12.1      Referencing Value Types

The unboxed form of a value type shall be referred to by using the valuetype keyword followed by a type reference.   The boxed form of a value type shall be referred to by using the boxed keyword followed by a type reference.

<valueTypeReference> ::=    

     boxed <typeReference> |

  valuetype <typeReference>

 

Implementation Specific (Microsoft)

For historical reasons “value class” may be used instead of “valuetype” although the latter is preferred.  V1 of the CLI does not support direct references to boxed value types; they should be treated as object instead.

12.2      Initializing Value Types

Like classes, value types may have both instance constructors (see clause 9.5.1) and type initializers (see clause 9.5.3). Unlike classes that are automatically initialized to null, however, the following rules constitute the only guarantee about the initilisation of (unboxed) value types:

·              Static variables shall be initialized to zero when a type is loaded (see clause 9.5.3.3), hence statics whose type is a value type are zero-initialized when the type is loaded.

·              Local variables shall be initialized to zero if the appropriate bit in the method header (see clause 24.4.4) is set.

·              Arrays shall be zero initialized.

·              Instances of classes (i.e. objects) shall be zero initialized prior to calling their instance constructor.

Rationale: Guaranteeing automatic initialization of unboxed value types is both difficult and expensive, especially on platforms that support thread-local storage and allow threads to be created outside of the CLI and then passed to the CLI for management.

 

Note: Boxed value types are classes and follow the rules for classes.

The instruction initobj (see Partition III_alink=Partition_III) performs zero-initialization under program control.  If a value type has a constructor, an instance of its unboxed type can be created as is done with classes. The newobj instruction (see Partition III_alink=Partition_III) is used along with the initializer and its parameters to allocate and initialize the instance. The instance of the value type will be allocated on the stack. The Base Class Library provides the method System.Array.Initialize (see Partition IV_alink=Partition_IV) to zero all instances in an array of unboxed value types.

Example (informative):

The following code declares and initializes three value type variables.  The first variable is zero-initialized, the second is initialized by calling an instance constructor, and the third by creating the object on the stack and storing it into the local.

.assembly Test { }

.assembly extern System.Drawing {

  .ver 1:0:3102:0

.publickeytoken = (b03f5f7f11d50a3a)

}

.method public static void Start()

{ .maxstack 3

  .entrypoint

  .locals init (valuetype [System.Drawing]System.Drawing.Size Zero,

valuetype [System.Drawing]System.Drawing.Size Init,

valuetype [System.Drawing]System.Drawing.Size Store)

 

  // Zero initialize the local named Zero

  ldloca Zero        // load address of local variable

  initobj valuetype [System.Drawing]System.Drawing.Size

 

  // Call the initializer on the local named Init

  ldloca Init // load address of local variable

  ldc.i4 425 // load argument 1 (width)

  ldc.i4 300 // load argument 2 (height)

  call instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32)

 

  // Create a new instance on the stack and store into Store.  Note that

  // stobj is used here – but one could equally well  use stloc, stfld, etc.

  ldloca Store

  ldc.i4 425 // load argument 1 (width)

  ldc.i4 300 // load argument 2 (height)

  newobj instance void [System.Drawing]System.Drawing.Size::.ctor(int32, int32)

  stobj valuetype [System.Drawing]System.Drawing.Size

  ret

}

12.3      Methods of Value Types

Value types may have static, instance and virtual methods. static methods of value types are defined and called the same way as static methods of class types.  As with classes, both instance and virtual methods of a boxed or unboxed value type may be called using the call instruction. The callvirt instruction shall not be used with unboxed value types, but it may be used on boxed value types.

Instance and virtual methods of classes shall be coded to expect a reference to an instance of the class as the this pointer.  By contrast, instance and virtual methods of value types shall be coded to expect a managed pointer  (see Partition I_alink=Partition_I) to an unboxed instance of the value type.  The CLI shall convert a boxed value type into a managed pointer to the unboxed value type when a boxed value type is passed as the this pointer to a virtual method whose implementation is provided by the unboxed value type.

Note: This operation is the same as unboxing the instance, since the unbox instruction (see Partition III_alink=Partition_III) is defined to return a managed pointer to the value type that shares memory with the original boxed instance.

The following diagrams may help understand the relationship between the boxed and unboxed representations of a value type.

 

 

Rationale: An important use of instance methods on value types is to change internal state of the instance.  This cannot be done if an instance of the unboxed value type is used for the this pointer, since it would be operating on a copy of the value, not the original value: unboxed value types are copied when they are passed as arguments.

Virtual methods are used to allow multiple types to share implementation code, and this requires that all classes that implement the virtual method share a common representation defined by the class that first introduces the method.  Since value types can (and in the Base Class Library do) implement interfaces and virtual methods defined on System.Object, it is important that the virtual method be callable using a boxed value type so  it can be manipulated as would any other type that implements the interface.  This leads to the requirement that the EE automatically unbox value types on virtual calls.

Table 1: Type of this given CIL instruction and declaring type of instance method.

 

Value Type (Boxed or Unboxed)

Interface

Class Type

call

managed pointer to value type

illegal

object reference

callvirt

managed pointer to value type

object reference

object reference

 

Example (informative):

The following converts an integer of the value type int32 into a string. Recall that int32 corresponds to the unboxed value type System.Int32 defined in the Base Class Library.  Suppose the integer is declared as:

     .locals init (int32 x)

Then the call is made as shown below:

     ldloca x          // load managed pointer to local variable

     call instance string
          valuetype [mscorlib]System.Convert::ToString()

However, if System.Object (a class) is used as the type reference rather than System.Int32 (a value type), the value of x shall be boxed before the call is made and the code becomes:

     ldloc x

     box valuetype [mscorlib]System.Int32

     callvirt instance string [mscorlib]System.Object::ToString()

13      Semantics of Special Types

Special Types are those that are referenced from CIL, but for which no definition is supplied: the VES supplies the definitions automatically based on information available from the reference.

13.1      Vectors

<type> ::= …

     | <type> [ ]

 

Vectors are single-dimension arrays with a zero lower bound.  They have direct support in CIL instructions (newarr, ldelem, stelem, and ldelema, see Partition III_alink=Partition_III).  The CIL Framework also provides methods that deal with multidimensional arrays, or single-dimension arrays with a non-zero lower bound (see Section 13.2). Two vectors are the same type if their element types are the same, regardless of their actual upper bounds.

Vectors have a fixed size and element type, determined when they are created.  All CIL instructions shall respect these values.  That is, they shall reliably detect attempts to index beyond the end of the vector, attempts to store the incorrect type of data into an element of a vector, and attempts to take addresses of elements of a vector with an incorrect data type.  See Partition III_alink=Partition_III.

Example (informative):

Declaring a vector of Strings:

     .field string[] errorStrings

Declaring a vector of function pointers:

     .field method instance void*(int32) [] myVec

Create a vector of 4 strings, and store it into the field errorStrings.  The four strings lie at errorStrings[0] through errorStrings[3]:

      ldc.i4.4

      newarr string

      stfld     string[] CountDownForm::errorStrings

Store the string "First" into errorStrings[0]:

     ldfld string[] CountDownForm::errorStrings

     ldc.i4.0

     ldstr "First"

     stelem

Vectors are subtypes of System.Array, an abstract class pre-defined by the CLI.  It provides several methods that can be applied to all vectors. See Partition IV_alink=Partition_IV.

13.2      Arrays

While vectors (see Section 13.1) have direct support through CIL instructions, all other arrays are supported by the VES by creating subtypes of the abstract class System.Arrray (see Partition IV_alink=Partition_IV)

<type> ::= …

   | <type> [ [<bound> [,<bound>]*] ]

 

The rank of an array is the number of dimensions.  The CLI does not support arrays with rank 0.  The type of an array (other than a vector) shall be determined by the type of its elements and the number of dimensions.

<bound> ::=

Description

  ...

lower and upper bounds unspecified.  In the case of multi-dimensional arrays, the ellipsis may be omitted

| <int32>

zero lower bound, <int32> upper bound

| <int32> ...

lower bound only specified

| <int32> ... <int32>

both bounds specified

 

The fundamental operations provided by the CIL instruction set for vectors are provided by methods on the class created by the VES.

The VES shall provide two constructors for arrays.  One takes a sequence of numbers giving the number of elements in each dimension (a lower bound of zero is assumed).  The second takes twice as many arguments: a sequence of lower bounds, one for each dimension; followed by a sequence of lengths, one for each dimension (where length is the number of elements required). 

In addition to array constructors, the VES shall provide the instance methods Get, Set, and Address to access specific elements and compute their addresses. These methods take a number for each dimension, to specify the target element.  In addition, Set takes an additional final argument specifying the value to store into the target element.

Example (informative):

Creates an array, MyArray, of strings with two dimensions, with indexes 5..10 and 3..7.  Stores the string "One" into MyArray[5, 3], retrieves it and prints it out. Then computes the address of MyArray[5, 4], stores "Test" into it, retrieves it, and prints it out.

.assembly Test { }

.assembly extern mscorlib { }

 

.method public static void Start()

{ .maxstack 5

  .entrypoint

  .locals (class [mscorlib]System.String[,] myArray)

 

  ldc.i4.5      // load lower bound for dim 1

  ldc.i4.6      // load (upper bound - lower bound + 1) for dim 1

  ldc.i4.3      // load lower bound for dim 2

  ldc.i4.5      // load (upper bound - lower bound + 1) for dim 2

  newobj instance void string[,]::.ctor(int32,

         int32, int32, int32)

  stloc  myArray

 

  ldloc myArray

  ldc.i4.5

  ldc.i4.3

  ldstr "One"

  call instance void string[,]::Set(int32, int32, string)

 

  ldloc myArray

  ldc.i4.5

  ldc.i4.3

  call instance string string[,]::Get(int32, int32)

  call void [mscorlib]System.Console::WriteLine(string)

 

  ldloc myArray

  ldc.i4.5

  ldc.i4.4

  call instance string & string[,]::Address(int32, int32)

  ldstr "Test"

  stind.ref

 

  ldloc myArray

  ldc.i4.5

  ldc.i4.4

  call instance string string[,]::Get(int32, int32)

  call void [mscorlib]System.Console::WriteLine(string)

 

  ret

}


The following text is informative

Whilst the elements of multi-dimensional arrays can be thought of as laid out in contiguous memory, arrays of arrays are different – each dimension (except the last) holds an array reference.  The following picture illustrates the difference:

       

On the left is a [6, 10] rectangular array.  On the right is not one, but a total of five arrays.  The vertical array is an array of arrays, and references the four horizontal arrays.  Note how the first and second elements of the vertical array both reference the same horizontal array.

Note that all dimensions of a multi-dimensional array shall be of the same size.  But in an array of arrays, it is possible to reference arrays of different sizes.  For example, the figure on the right shows the vertical array referencing arrays of lengths 8, 8, 3, null, 6 and 1.

There is no special support for these so-called jagged arrays in either the CIL instruction set or the VES.  They are simply vectors whose elements are themselves either the base elements or (recursively) jagged arrays.

End of informative text


13.3      Enums

An enum, short for enumeration, defines a set of symbols that all have the same type.  A type shall be an enum if and only if it has an immediate base type of System.Enum.  Since System.Enum itself has an immediate base type of System.ValueType (see Partition IV_alink=Partition_IV), enums are value types (see Chapter 12).  The symbols of an enum are represented by an underlying type: one of { bool, char, int8, unsigned int8, int16, unsigned int16, int32, unsigned int32, int64, unsigned int64, float32, float64, native int, unsigned native int }

Note: The CLI does not provide a guarantee that values of the enum type are integers corresponding to one of the symbols (unlike Pascal).  In fact, the CLS (see Partition I_alink=Partition_I, CLS) defines a convention for using enums to represent bit flags which can be combined to form integral value that are not named by the enum type itself.

Enums obey additional restrictions beyond those on other value types.  Enums shall contain only fields as members (they shall not even define type initializers or instance constructors); they shall not implement any interfaces; they shall have auto field layout (see clause 9.1.2); they shall have exactly one instance field and it shall be of the underlying type of the enum; all other fields shall be static and literal (see Section 15.1); and they shall not be initialized with the initobj instruction.

Rationale: These restrictions allow a very efficient implementation of enums.

The single, required, instance field stores the value of an instance of the enum. The static literal fields of an enum declare the mapping of the symbols of the enum to the underlying values.  All of these fields shall have the type of the enum and shall have field init metadata that assigns them a value (see Section 15.2).

For binding purposes (e.g. for locating a method definition from the method reference used to call it) enums shall be distinct from their underlying type.  For all other purposes, including verification and execution of code, an unboxed enum freely interconverts with its underlying type.  Enums can be boxed (see Chapter 12) to a corresponding boxed instance type, but this type is not the same as the boxed type of the underlying type, so boxing does not lose the original type of the enum.

Example (informative):

Declare an enum type, then create a local variable of that type.  Store a constant of the underlying type into the enum (showing automatic coercsion from the underlying type to the enum type).  Load the enum back and print it as the underlying type (showing automatic coersion back).  Finally, load the address of the enum and extract the contents of the instance field and print that out as well.

.assembly Test { }

.assembly extern mscorlib { }

 

.class sealed public ErrorCodes extends [mscorlib]System.Enum

{ .field public unsigned int8 MyValue

  .field public static literal valuetype ErrorCodes no_error = int8(0)

  .field public static literal valuetype ErrorCodes format_error =

         int8(1)

  .field public static literal valuetype ErrorCodes overflow_error =

         int8(2)

  .field public static literal valuetype ErrorCodes nonpositive_error =

         int8(3)

}

 

.method public static void Start()

{ .maxstack 5

  .entrypoint

  .locals init (valuetype ErrorCodes errorCode)

 

ldc.i4.1           // load 1 (= format_error)

  stloc errorCode // store in local, note conversion to enum

  ldloc errorCode

  call void [mscorlib]System.Console::WriteLine(int32)

  ldloca errorCode // address of enum

  ldfld unsigned int8 valuetype ErrorCodes::MyValue

  call void [mscorlib]System.Console::WriteLine(int32)

  ret

}

13.4      Pointer Types

<type> ::= …

Section

   | <type> &

13.4.2

   | <type> *

13.4.1

 

A pointer type shall be defined by specifying a signature that includes the type for the location it points at.  A pointer may be managed (reported to the CLI garbage collector, denoted by &, see clause 13.4.2) or unmanaged (not reported, denoted by *, see clause 13.4.1)

Pointers may contain the address of a field (of an object or value type) or an element of an array.  Pointers differ from object references in that they do not point to an entire type instance, but rather to the interior of an instance.  The CLI provides two type-safe operations on pointer:

·              loading the value from the location referenced by the pointer

·              storing an assignment-compatible value into the location referenced  by the pointer

For pointers into the same array or object (see Partition I_alink=Partition_I) the following arithmetic operations are supported:

·              Adding an integer value to a pointer, where that value is interpreted as a number of bytes, results in a pointer of the same kind

·              Subtracting an integer value (number of bytes) from a pointer results in a pointer of the same kind. Note that subtracting a pointer from an integer value is not permitted. 

·              Two pointers, regardless of kind, can be subtracted from one another, producing an integer value that specifies the number of bytes between the addresses they reference.


The following is informative text

Pointers are compatible with unsigned int32 on 32-bit architectures, and with unsigned int64 on 64-bit architectures.  They are best considered as unsigned int, whose size varies depending upon the runtime machine architecture.

The CIL instruction set (see Partition III_alink=Partition_III) contains instructions to compute addresses of fields, local variables, arguments, and elements of vectors:

Instruction

Description

ldarga

Load address of argument

ldelema

Load address of vector element

ldflda

Load address of field

ldloca

Load address of local variable

ldsflda

Load address of static field

 

Once a pointer is loaded onto the stack, the ldind class of instructions may be used to load the data item to which it points. Similarly, the stind class of instructions can be used to store data into the location.

Note that the CLI will throw an InvalidOperationException for an ldflda instruction if the address is not within the current application domain. This situation arises typically only from the use of objects with a base type of System.MarshalByRefObject (see Partition IV_alink=Partition_IV).

13.4.1      Unmanaged Pointers

Unmanaged pointers (*) are the traditional pointers used in languages like C and C++. There are no restrictions on their use, although for the most part they result in code that cannot be verified. While it is perfectly legal to mark locations that contain unmanaged pointers as though they were unsigned integers (and this is, in fact, how they are treated by the VES), it is often better to mark them as unmanaged pointers to a specific type of data. This is done by using * in a signature for a return value, local variable or an argument or by using a pointer type for a field or array element.

·              Unmanaged pointers are not reported to the garbage collector and can be used in any way that an integer can be used.

·              Verifiable code cannot dereference unmanaged pointers.

·              Unverified code can pass an unmanaged pointer to a method that expects a managed pointer. This is safe only if one of the following is true:

a.            The unmanaged pointer refers to memory that is not in memory used by the CLI for storing instances of objects (“garbage collected memory” or “managed memory”).

b.            The unmanaged pointer contains the address of a field within an object.

c.            The unmanaged pointer contains the address of an element within an array.

d.            The unmanaged pointer contains the address where the element following the last element in an array would be located

13.4.2      Managed Pointers

Managed pointers (&) may point to an instance of a value type, a field of an object, a field of a value type, an element of an array, or the address where an element just past the end of an array would be stored (for pointer indexes into managed arrays). Managed pointers cannot be null, and they shall be reported to the garbage collector even if they do not point to managed memory. 

Managed pointers are specified by using & in a signature for a return value, local variable or an argument or by using a by-ref type for a field or array element.

·              Managed pointers can be passed as arguments, stored in local variables, and returned as values.

·              If a parameter is passed by reference, the corresponding argument is a managed pointer.

·              Managed pointers cannot be stored in static variables, array elements, or fields of objects or value types.

·              Managed pointers are not interchangeable with object references. 

·              A managed pointer cannot point to another managed pointer, but it can point to an object reference or a value type.

·              A managed pointer can point to a local variable, or a method argument

·              Managed pointers that do not point to managed memory can be converted (using conv.u or conv.ovf.u) into unmanaged pointers, but this is not verifiable. 

e.            Unverified code that erroneously converts a managed pointer into an unmanaged pointer can seriously compromise the integrity of the CLI. See Partition III_alink=Partition_III (Managed Pointers) for more details.

End informative text


13.5      Method Pointers

<type> ::= …

   | method <callConv> <type> * ( <parameters> )

 

Variables of type method pointer shall store the address of the entry point to a method with compatible signature.  A pointer to a static or instance method is obtained with the ldftn instruction, while a pointer to a virtual method is obtained with the ldvirtftn instruction.  A method may be called by using a method pointer with the calli instruction.  See Partition III_alink=Partition_III for the specification of these instructions.

Note: Like other pointers, method pointers are compatible with unsigned int64 on 64-bit architectures with unsigned int32 and on 32-bit architectures.  The preferred usage, however, is unsigned native int, which works on both 32- and 64-bit architectures.

Example (informative):

Call a method using a pointer.  The method MakeDecision::Decide returns a method pointer to either AddOne or Negate, alternating on each call.  The main program call MakeDecision::Decide three times and after each call uses a CALLI instruction to call the method specified.  The output printed is "-1 2 –1" indicating successful alternating calls.

.assembly Test { }

.assembly extern mscorlib { }

 

.method public static int32 AddOne(int32 Input)

{ .maxstack 5

  ldarg Input

  ldc.i4.1

  add

  ret

}

 

.method public static int32 Negate(int32 Input)

{ .maxstack 5

  ldarg Input

  neg

  ret

}

 

.class value sealed public MakeDecision extends

         [mscorlib]System.ValueType

{ .field static bool Oscillate

  .method public static method int32 *(int32) Decide()

  { ldsfld bool valuetype MakeDecision::Oscillate

    dup

    not

    stsfld bool valuetype MakeDecision::Oscillate

    brfalse NegateIt

    ldftn int32 AddOne(int32)

    ret

NegateIt:

    ldftn int32 Negate(int32)

    ret

  }

}

 

.method public static void Start()

{ .maxstack 2

  .entrypoint

 

  ldc.i4.1

  call method int32 *(int32) valuetype MakeDecision::Decide()

  calli int32(int32)

  call  void [mscorlib]System.Console::WriteLine(int32)

 

  ldc.i4.1

  call method int32 *(int32) valuetype MakeDecision::Decide()

  calli int32(int32)

  call  void [mscorlib]System.Console::WriteLine(int32)

 

  ldc.i4.1

  call method int32 *(int32) valuetype MakeDecision::Decide()

  calli int32(int32)

  call  void [mscorlib]System.Console::WriteLine(int32)

 

  ret

}

13.6      Delegates

Delegates (see Partition I_alink=Partition_I) are the object-oriented equivalent of function pointers. Unlike function pointers, delegates are object-oriented, type-safe, and secure.  Delegates are reference types, and are declared in the form of Classes.  Delegates shall have an immediate base type of System.MulticastDelegate, which in turns has an immediate base type of System.Delegate (see Partition IV_alink=Partition_IV).

Delegates shall be declared sealed, and the only members a Delegate shall have are either two or four methods as specified here. These methods shall be declared runtime and managed (see clause 14.4.3). They shall not have a body, since it shall be automatically created by the VES. Other methods available on delegates are inherited from the classes System.Delegate and System.MulticastDelegate in the Base Class Library (see Partition IV_alink=Partition_IV).

Rationale: A better design would be to simply have delegate classes derive directly from System.Delegate.  Unfortunately, backward compatibility with an existing CLI does not permit this design.

The instance constructor (named .ctor and marked specialname and rtspecialname, see clause 9.5.1) shall take exactly two parameters. The first parameter shall be of type System.Object and the second parameter shall be of type System.IntPtr.  When actually called (via a newobj instruction, see Partition III_alink=Partition_III), the first argument shall be an instance of the class (or one of its subclasses) that defines the target method and the second argument shall be a method pointer to the method to be called.

The Invoke method shall be virtual and have the same signature (return type, parameter types, calling convention, and modifiers, see Section 7.1) as the target method. When actually called the arguments passed shall match the types specified in this signature.

The BeginInvoke method (see clause 13.6.2.1), if present, shall be virtual have a signature related to, but not the same as, that of the Invoke method.  There are two differences in the signature.   First, the return type shall be System.IAsyncResult (see Partition IV_alink=Partition_IV). Second, there shall be two additional parameters that follow those of Invoke: the first of type System.AsyncCallback and the second of type System.Object.

The EndInvoke method (see clause 13.6.2) shall be virtual have the same return type as the Invoke method. It shall take as parameters exactly those parameters of Invoke that are managed pointers, in the same order they occur in the signature for Invoke.  In addition, there shall be an additional parameter of type System.IAsyncResult.

Example (informative):

The following example declares a Delegate used to call functions that take a single integer and return void.  It provides all four methods so it can be called either synchronously or asynchronously.  Because there are no parameters that are passed by reference (i.e. as managed pointers) there are no additional arguments to EndInvoke.

.assembly Test { }

.assembly extern mscorlib { }

 

.class private sealed StartStopEventHandler

       extends [mscorlib]System.MulticastDelegate

 { .method public specialname rtspecialname instance

           void .ctor(object Instance, native int Method)

runtime managed {}

   .method public virtual void Invoke(int32 action) runtime managed {}

   .method public virtual

      class [mscorlib]System.IAsyncResult

        BeginInvoke(int32 action,

class [mscorlib]System.AsyncCallback callback,

object Instance) runtime managed {}

   .method public virtual

      void EndInvoke(class [mscorlib]System.IAsyncResult result)

      runtime managed {}

}

As with any class, an instance is created using the  newobj instruction in conjunction with the instance constructor.  The first argument to the constructor shall be the object on which the method is to be called, or it shall be null if the method is a static method.  The second argument shall be a method pointer to a method on the corresponding class and with a signature that matches that of the delegate class being instantiated.

Implementation-Specific (Microsoft)

The Microsoft implementation of the CLI allows the programmer to add more methods to a delegate, on the condition that they provide an implementation for those methods (ie, they cannot be marked runtime).  Note that such use makes the resulting assembly non-portable.

13.6.1      Synchronous Calls to Delegates

The synchronous mode of calling delegates corresponds to regular method calls and is performed by calling the virtual method named Invoke on the delegate. The delegate itself is the first argument to this call (it serves as the this pointer), followed by the other arguments as specified in the signature.  When this call is made, the caller shall block until the called method returns. The called method shall be executed on the same thread as the caller.

Example (informative):

Continuing the previous example, define a class Test that declares a method, onStartStop, appropriate for use as the target for the delegate.

 

.class public Test

{ .field public int32 MyData

  .method public void onStartStop(int32 action)

  { ret        // put your code here

  }

  .method public specialname rtspecialname
          instance void .ctor(int32
Data)

  { ret        // call parent constructor, store state, etc.

  }

}

 

Then define a main program. This one constructs an instance of Test and then a delegate that targets the onStartStop method of that instance.  Finally, call the delegate.

 

.method public static void Start()

{ .maxstack 3

  .entrypoint

  .locals (class StartStopEventHandler DelegateOne,

           class Test InstanceOne)

  // Create instance of Test class

  ldc.i4.1

  newobj instance void Test::.ctor(int32)

  stloc InstanceOne

  // Create delegate to onStartStop method of that class

  ldloc InstanceOne

  ldftn instance void Test::onStartStop(int32)

  newobj void StartStopEventHandler::.ctor(object, native int)

  stloc DelegateOne

  // Invoke the delegate, passing 100 as an argument

  ldloc DelegateOne

  ldc.i4 100

  callvirt instance void StartStopEventHandler::Invoke(int32)

  ret

}

  // Note that the example above creates a delegate to a non-virtual

  // function.  If onStartStop had instead been a virtual function, use

  // the following code sequence instead :

 

  ldloc InstanceOne

  dup

  ldvirtftn instance void Test::onStartStop(int32)

  newobj void StartStopEventHandler::.ctor(object, native int)

  stloc DelegateOne

  // Invoke the delegate, passing 100 as an argument

  ldloc DelegateOne

Note: The code sequence above shall use dup –not ldloc InstanceOne twice.  The dup code sequence is easily recognized as typesafe, whereas alternatives would require more complex analysis.  Verifiability of code is discussed in Partition III_alink=Partition_III

13.6.2      Asynchronous Calls to Delegates

In the asynchronous mode, the call is dispatched, and the caller shall continue execution without waiting for the method to return. The called method shall be executed on a separate thread.

To call delegates asynchronously, the BeginInvoke and EndInvoke methods are used.

Note: if the caller thread terminates before the callee completes, the callee thread is unaffected.  The callee thread continues execution and terminates silently

Note: the callee may throw exceptions.  Any unhandled exception propagates to the caller via the EndInvoke method.

13.6.2.1       The BeginInvoke Method

An asynchronous call to a delegate shall begin by making a virtual call to the BeginInvoke method.  BeginInvoke is similar to the Invoke method (see clause 13.6.1), but has three differences:

·              It has a two additional parameters, appended to the list, of type System.AsyncCallback, and System.Object

·              The return type of the method is System.IAsyncResult

Although the BeginInvoke method therefore includes parameters that represent return values, these values are not updated by this method.  The results instead are obtained from the EndInvoke method (see below).

Unlike a synchronous call, an asynchronous call shall provide a way for the caller to determine when the call has been completed.  The CLI provides two such mechanisms.  The first is through the result returned from the call.  This object, an instance of the interface System.IAsyncResult,  can be used to wait for the result to be computed, it can be queried for the current status of the method call, and it contains the System.Object value that was passed to the call to BeginInvoke.  See Partition IV_alink=Partition_IV.

The second mechanism is through the System.AsyncCallback delegate passed to BeginInvoke. The VES shall call this delegate when the value is computed or an exception has been raised indicating that the result will not be available.  The value passed to this callback is the same value passed to the call to BeginInvoke.  A value of null may be passed for System.AsyncCallback to indicate that the VES need not provide the callback.

Rationale: This model supports both a polling approach (by checking the status of the returned System.IAsyncResult) and an event-driven approach (by supplying a System.AsyncCallback) to asynchronous calls.

A synchronous call returns information both through its return value and through output parameters.  Output parameters are represented in the CLI as parameters with managed pointer type.  Both the returned value and the values of the output parameters are not available until the VES signals that the asynchronous call has completed successfully.  They are retrieved by calling the EndInvoke method on the delegate that began the asynchronous call.

13.6.2.2       The EndInvoke Method

The EndInvoke method can be called at any time after BeginInvoke.   It shall suspend the thread that calls it until the asynchronous call completes.  If the call completes successfully, EndInvoke will return the value that would have been returned had the call been made synchronously, and its managed pointer arguments will point to values that would have been returned to the out parameters of the synchronous call.

EndInvoke requires as parameters the value returned by the originating call to BeginInvoke (so that different calls to the same delegate can be distinguished, since they may execute concurrently) as well as any managed pointers that were passed as arguments (so their return values can be provided).

14      Defining, Referencing, and Calling Methods

Methods may be defined at the global level (outside of any type):

<decl> ::= …

   | .method <methodHead> { <methodBodyItem>* }

 

as well as inside a type:

<classMember> ::= …

   | .method <methodHead> { <methodBodyItem>* }

 

14.1      Method Descriptors

There are four constructs in ilasm connected with methods.  These correspond with different metadata constructs, as described in Chapter 21.

14.1.1      Method Declarations

A MethodDecl, or method declaration, supplies the method name and signature (parameter and return types), but not its body.  That is, a method declaration provides a <methodHead> but no <methodBodyItem>s.  These are used at callsites to specify the call target (call or callvirt instructions, see Partition III_alink=Partition_III) or to declare an abstract method.  A MethodDecl has no direct logical couterpart in the metadata; it can be either a Method or a MethodRef.

14.1.2      Method Definitions

A Method, or method definition, supplies the method name, attributes, signature and body.  That is, a method definition provides a <methodHead> as well as one or more <methodBodyItem>s.  The body includes the method's CIL instructions, exception handlers, local variable information, and additional runtime or custom metadata about the method.  See Chapter 11.

14.1.3      Method References

A MethodRef, or method reference, is a reference to a method. It is used when a method is called whose definition lies in another module or assembly.  A MethodRef shall be resolved by the VES into a Method before the method is called at runtime.  If a matching Method cannot be found, the VES shall throw a System.MissingMethodException.  See Chapter 21.23.

14.1.4      Method Implementations

A MethodImpl, or method implementation, supplies the executable body for an existing virtual method.  It associates a Method (representing the body) with a MethodDecl or Method (representing the virtual method).  A MethodImpl is used to provide an implementation for an inherited virtual method or a virtual method from an interface when the default mechanism (matching by name and signature) would not provide the correct result.  See Section 21.25.

14.2      Static, Instance, and Virtual Methods

Static methods are methods that are associated with a type, not with its instances.

Instance methods are associated with an instance of a type: within the body of an instance method it is possible to reference the particular instance on which the method is operating (via the this pointer). It follows that instance methods may only be defined in classes or value types, but not in interfaces or outside of a type (globally).  However, notice

1.             instance methods on classes (including boxed value types), have a this pointer that is by default an object reference to the class on which the method is defined

2.             instance methods on (unboxed) value types, have a this pointer that is by default a managed pointer to an instance of the type on which the method is defined

3.             there is a special encoding (denoted by the syntactic item explicit in the calling convention, see Section 14.3) to specify the type of the this pointer, overriding the default values specified here

4.             the this pointer may be null

Virtual methods are associated with an instance of a type in much the same way as for instance methods. However, unlike instance methods, it is possible to call a virtual method in such a way that the implementation of the method shall be chosen at runtime by the VES depends upon the type of object used for the this pointer.  The particular Method that implements a virtual method is determined dynamically at runtime (a virtual call) when invoked via the callvirt instruction; whilst the binding is decided at compile time when invoked via the call instruction (see Partition III_alink=Partition_III).

With virtual calls (only) the notion of inheritance becomes important.  A subclass may override a virtual method inherited from its base classes, providing a new implementation of the method.  The method attribute newslot specifies that the CLI shall not override the virtual method definition of the base type, but shall treat the new definition as an independent virtual method definition. 

Abstract virtual methods (which shall only be defined in abstract classes or interfaces) shall be called only with a callvirt instruction.  Similarly, the address of an abstract virtual method shall be computed with the ldvirtftn instruction, and the ldftn instruction shall not be used.

Rationale: With a concrete virtual method there is always an implementation available from the class that contains the definition, thus there is no need at runtime to have an instance of a class available.  Abstract virtual methods, however, receive their implementation only from a subtype or a class that implements the appropriate interface, hence an instance of a class that actually implements the method is required.

14.3      Calling Convention

<callConv> ::= [instance [explicit]] [<callKind>]

 

A calling convention specifies how a method expects its arguments to be passed from the caller to the called method.   It consists of two parts; the first deals with the existence and type of the this pointer, while the second relates to the mechanism for transporting the arguments.

If the attribute instance is present it indicates that a this pointer shall be passed to the method.  It shall be used for both instance and virtual methods.

Implementation Specific (Microsoft)

For simplicity, the assembler automatically sets or clears the instance bit in the calling convention for a method definition based on the method attributes static and virtual.  In a method reference, however, the instance bit shall be specified directly since the information about static or virtual is not captured in a reference.

Normally, a parameter list  (which always follows the calling convention) does not provide information about the type of the this pointer, since this can be deduced from other information.  When the combination instance explicit is specified, however, the first type in the subsequent parameter list specifies the type of the this pointer and subsequent entries specify the types of the parameters themselves.

<callKind> ::=

  default

| unmanaged cdecl

| unmanaged fastcall

| unmanaged stdcall

| unmanaged thiscall

| vararg

 

Managed code shall have only the default or vararg calling kind.  default shall be used in all cases except when a method accepts an arbitrary number of arguments, in which case vararg shall be used.

When dealing with methods implemented outside the CLI it is important to be able to specify the calling convention required.  For this reason there are 16 possible encodings of the calling kind.  Two are used for the managed calling kinds.  Four are reserved with defined meaning across many platforms:

·              unmanaged cdecl is the calling convention used by standard C

·              unmanaged stdcall specifies a standard C++ call

·              unmanaged fastcall is a special optimized C++ calling convention

·              unmanaged thiscall is a C++ call that passes a this pointer to the method

Four more are reserved for existing calling conventions, but their use is not portable.  Four more are reserved for future standardization, and two are available for non-standard experimental use.

(By "portable" is meant a feature that is available on all conforming implementations of the CLI)

14.4      Defining Methods

<methodHead> ::=

  <methAttr>* [<callConv>] [<paramAttr>*] <type>

              [marshal ( [<nativeType>] )]

              <methodName> ( <parameters> ) <implAttr>*

 

The method head (see also Chapter 11) consists of

·              the calling convention (<callConv>, see Section 14.3)

·              any number of predefined method attributes (<paramAttr>, see clause 14.4.2)

·              a return type with optional attributes

·              optional marshalling information (see Section 7.4)

·              a method name

·              a signature

·              and any number of implementation attributes (<implAttr>, see clause 14.4.3)

Methods that do not have a return value shall use void as the return type.

<methodName> ::=

  .cctor

| .ctor

| <dottedname>

 

Method names are either simple names or the special names used for instance constructors and type initializers.

<parameters> ::= [<param> [, <param>]*]

<param> ::=

  ...

| [<paramAttr>*] <type> [marshal ( [<nativeType>] )] [<id>]

 

The <id>, if present, is the name of the parameter. A parameter may be referenced either by using its name or the zero-based index of the parameter.  In CIL instructions it is always encoded using the zero-based index (the name is for ease of use in ilasm).

Note that, in contrast to calling a vararg method, the definition of a vararg method does not include any ellipsis (“”)

<paramAttr> ::=

  [in]

| [opt]

| [out]

 

The parameter attributes shall be attached to the parameters (see Section 21.30) and hence are not part of a method signature.

Note: Unlike parameter attributes, custom modifiers (modopt and modreq) are part of the signature.  Thus, modifiers form part of the method’s contract while parameter attributes are not.

in and out shall only be attached to parameters of pointer (managed or unmanaged) type.  They specify whether the parameter is intended to supply input to the method, return a value from the method, or both.  If neither is specified in is assumed.  The CLI itself does not enforce the semantics of these bits, although they may be used to optimize performance, especially in scenarios where the call site and the method are in different application domains, processes, or computers.

opt specifies that this parameter is intended to be optional from an end-user point of view.  The value to be supplied is stored using the .param syntax (see clause 14.4.1.4).

14.4.1      Method Body

The method body shall contain the instructions of a program. However, it may also contain labels, additional syntactic forms and many directives that provide additional information to ilasm and are helpful in the compilation of methods of some languages.

<methodBodyItem> ::=

Description

Section

  .custom <customDecl>

Definition of custom attributes.

20

| .data <datadecl>

Emits data to the data section

15.3

| .emitbyte <unsigned int8>

Emits a byte to the code section of the method.

14.4.1.1

| .entrypoint

Specifies that this method is the entry point to the application (only one such method is allowed).

14.4.1.2

| .locals [init]

     ( <localsSignature> )

Defines a set of local variables for this method.

14.4.1.3

| .maxstack <int32>

int32 specifies the maximum number of elements on the evaluation stack during the execution of the method

14.4.1

| .override <typeSpec>::<methodName>

Use current method as the implementation for the method specified.

9.3.2

| .param [ <int32> ]

          [= <fieldInit>]

Store a constant <fieldInit> value for parameter <int32>

14.4.1.4

| <externSourceDecl>

.line or #line

5.7

| <instr>

An instruction

Partition V_alink=Partition_V

| <id> :

A label

5.4

| <scopeBlock>

Lexical scope of local variables

14.4.4

| <securityDecl>

.permission or .permissionset

19

| <sehBlock>

An exception block

18

 

14.4.1.1       .emitbyte

<methodBodyItem> ::= …

   | .emitbyte <unsigned int8>

 

Emits an unsigned 8 bit value directly into the CIL stream of the method. The value is emitted at the position where the directive appears.

Note: the .emitbyte directive is used for generating tests.  It is not required in generating regular programs

14.4.1.2       .entrypoint

<methodBodyItem> ::= …

   | .entrypoint

 

The .entrypoint directive marks the current method, which shall be static, as the entry point to an application. The VES shall call this method to start the application. An executable shall have exactly one entry point method. This entry point method may be a global method or may appear inside a type.  (The effect of the directive is to place the metadata token for this method into the CLI header of the PE file)

The entry point method shall either accept no arguments or a vector of strings. If it accepts a vector of strings, the strings shall represent the arguments to the executable, with index 0 containing the first argument.  The mechanism for specifying these arguments is platform-specific and is not specified here.

The return type of the entry point method shall be void, int32, or unsigned int32. If an int32 or unsigned int32 is returned, the executable may return an exit code to the host environment. A value of 0 shall indicate that the application terminated ordinarily.

The accessibility of the entry point method shall not prevent its use in starting execution.  Once started the VES shall treat the entry point as it would any other method.

Example (informative):

The following example prints the first argument and return successfully to the operating system:

.method public static int32 MyEntry(string[] s) CIL managed

{ .entrypoint

  .maxstack 2

  ldarg.0                     // load and print the first argument

  ldc.i4.0

  ldelem.ref

  call void [mscorlib]System.Console::WriteLine(string)

  ldc.i4.0                    // return success

  ret

}

14.4.1.3       .locals

The .locals statement declares local variables (see Partition I_alink=Partition_I) for the current method.

<methodBodyItem> ::= …

   | .locals [init] ( <localsSignature> )

<localsSignature> ::= <local> [, <local>]*

<local> ::= <type> [<id>]

 

The <id>, if present, is the name of the local.

If init is specified, the variables are initialized to their default values according to their type. Reference types are initialized to null and value types are zeroed out.

Note: Verifiable methods shall include the init keyword. See Partition III_alink=Partition_III.

Implementation Specific (Microsoft)

ilasm allows nested local variable scopes to be provided and allows locals in nested scopes to share the same location as those in the outer scope. The information about local names, scoping, and overlapping of scoped locals is persisted to the PDB (debugger symbol) file rather than the PE file itself.

<local> ::= [[<int32>]] <type> [<id>]

The integer in brackets that precedes the <type>, if present, specifies the local number (starting with 0) being described. This allows nested locals to reuse the same location as a local in the outer scope.  It is not legal to overlap two local variables unless they have the same type. When no explicit index is specified, the next unused index is chosen. That is, two locals never share and index unless the index is given explicitly.

If init is used, all local variables will be initialized to their default values, even variables in another .locals directive in the same method, which does not have the init directive.

14.4.1.4       .param

<methodBodyItem> ::= …

   | .param [ <int32> ] [= <fieldInit>]

 

Stores in the metadata a constant value associated with method parameter number <int32>, see Section 21.9. While the CLI requires that a value be supplied for the parameter, some tools may use the presence of this attribute to indicate that the tool rather than the user is intended to supply the value of the parameter.   Unlike CIL instructions, .param uses index 0 to specify the return value of the method, index 1 is the first parameter of the method, and so forth.

Note: The CLI attaches no semantic whatsoever to these values – it is entirely up to compilers to implement any semantic they wish (eg so-called default argument values)

14.4.2      Predefined Attributes on Methods

<methAttr> ::=

Description

Section

  abstract

The method is abstract (shall also be virtual).

14.4.2.4

| assembly

Assembly accessibility

14.4.2.1

| compilercontrolled

Compiler-controlled accessibility.

14.4.2.1

| famandassem

Family and Assembly accessibility

14.4.2.1

| family

Family accessibility

14.4.2.1

| famorassem

Family or Assembly accessibility

14.4.2.1

| final

This virtual method cannot be overridden by subclasses.

14.4.2.2

| hidebysig

Hide by signature. Ignored by the runtime.

14.4.2.2

| newslot

Specifies that this method shall get a new slot in the virtual method table.

14.4.2.3

| pinvokeimpl (
   
<QSTRING> [as <QSTRING>]
    <pinvAttr>* )

Method is actually implemented in native code on the underlying platform

14.4.2.5

| private

Private accessibility

14.4.2.1

| public

Public accessibility.

14.4.2.1

| rtspecialname

The method name needs to be treated in a special way by the runtime.

14.4.2.6

| specialname

The method name needs to be treated in a special way by some tool.

14.4.2.6

| static

Method is static.

14.4.2.2

| virtual

Method is virtual.

14.4.2.2

 

Implementation Specific (Microsoft)

The following syntax is supported:

    <methAttr> ::= … | unmanagedexp | reqsecobj

unmanagedexp indicates that the method is exported to unmanaged code using COM interop; reqsecobj indicates that the method calls another method with security attributes.

Note that in the first release, ilasm does not recognize the compilercontrolled keyword.  Instead, use privatescope.

The following combinations of predefined attributes are illegal:

·         static combined with any of final, virtual, or newslot

·         abstract combined with any of final or pinvokeimpl

·         compilercontrolled combined with any of virtual, final, specialname or rtspecialname

14.4.2.1       Accessibility Information

<methAttr> ::= …

| assembly

| compilercontrolled

| famandassem

| family

| famorassem

| private

| public

 

Only one of these attributes shall be applied to a given method. See Partition I_alink=Partition_I.

14.4.2.2       Method Contract Attributes

<methAttr> ::= …

| final

| hidebysig

| static

| virtual

 

These attributes may be combined, except a method shall not be both static and virtual; only virtual methods may be final; and abstract methods shall not be final.

final methods shall not be overridden by subclasses of this type.

hidebysig is supplied for the use of tools and is ignored by the VES.  It specifies that the declared method hides all methods of the parent types that have a matching method signature; when omitted the method should hide all methods of the same name, regardless of the signature.

Rationale: Some languages use a hide-by-name semantics (C++) while others use a hide-by-name-and-signature semantics (C#, Java™)

Static and virtual are described in Section 14.2.

14.4.2.3       Overriding Behavior

<methAttr> ::= …

   | newslot

 

newslot shall only be used with virtual methods. See Section 9.3.

14.4.2.4       Method Attributes

<methAttr> ::= …

   | abstract

 

abstract shall only be used with virtual methods that are not final. It specifies that an implementation of the method is not provided but shall be provided by a subclass.  Abstract methods shall only appear in abstract types (see clause 9.1.4).

14.4.2.5       Interoperation Attributes

<methAttr> ::= …

   | pinvokeimpl ( <QSTRING> [as <QSTRING>] <pinvAttr>* )

 

See clause 14.5.2and Section 21.20.

14.4.2.6       Special Handling Attributes

<methAttr> ::= …

   | rtspecialname

   | specialname

 

The attribute rtspecialname specifies that the method name shall be treated in a special way by the runtime. Examples of special names are .ctor (object constructor) and .cctor (type initializer).

specialname indicates that the name of this method has special meaning to some tools.

14.4.3      Implementation Attributes of Methods

<implAttr> ::=

Description

Section

  cil

The method contains standard CIL code.

14.4.3.1

| forwardref

The body of this method is not specified with this declaration.

14.4.3.3

| internalcall

Denotes the method body is provided by the CLI itself

14.4.3.3

| managed

The method is a managed method.

14.4.3.2

| native

The method contains native code.

14.4.3.1

| noinlining

The runtime shall not expand the method inline.

14.4.3.3

| runtime

The body of the method is not defined but produced by the runtime.

14.4.3.1

| synchronized

The method shall be executed in a single threaded fashion.

14.4.3.3

| unmanaged

Specifies that the method is unmanaged.

14.4.3.2

 

Implementation Specific (Microsoft)

The following syntax is accepted:

<implAttr> ::= … | preservesig

preservesig specifies the method signature is mangled to return HRESULT, with the return value as a parameter.

14.4.3.1       Code Implementation Attributes

<implAttr> ::= …

   | cil

   | native

   | runtime

 

These attributes are exclusive, they specify the type of code the method contains.

cil specifies that the method body consists of cil code. Unless the method is declared abstract, the body of the method shall be provided if cil is used.

native specifies that a method was implemented using native code, tied to a specific processor for which it was generated. native methods shall not have a body but instead refer to a native method that declares the body. Typically, the PInvoke functionality (see clause 14.5.2) of the CLI is used to refer to a native method.

runtime specifies that the implementation of the method is automatically provided by the runtime and is primarily used for the method of delegates (see Section 13.6).

14.4.3.2       Managed or Unmanaged

<implAttr> ::= …

   | managed

   | unmanaged

 

These shall not be combined.  Methods implemented using CIL are managed.  Unmanaged is used primarily with PInvoke (see clause 14.5.2).

14.4.3.3       Implementation Information

<implAttr> ::= …

   | forwardref

   | internalcall

   | noinlining

   | synchronized

 

These attributes may be combined.

forwardref specifies that the body of the method is provided elsewhere.  This attribute shall not be present when an assembly is loaded by the VES.  It is used for tools (like a static linker) that will combine separately compiled modules and resolve the forward reference.

internalcall specifies that the method body is provided by this CLI (and is typically used by low-level methods in a system library).  It shall not be applied to methods that are intended for use across implementations of the CLI. 

Implementation Specific (Microsoft)

internalcall allows the lowest level parts of the Base Class Library to wrap unmanaged code built into the CLI.

noinlining specifies that the body of this method should not be included into the code of any caller methods, by a CIL-to-native-code compiler; it shall be kept as a separate routine.

Rationale: specifying that a method not be inlined ensures that it remains 'visible' for debugging (eg displaying stack traces) and profiling.  It also provides a mechanism for the programmer to override the default heuristics a CIL-to-native-code compiler uses for inlining.

synchronized specifies that the whole body of the method shall be single threaded. If this method is an instance or virtual method a lock on the object shall be obtained before the method is entered. If this method is a static method a lock on the type shall be obtained before the method is entered. If a lock cannot be obtained the requesting thread shall not proceed until it is granted the lock. This may cause deadlocks. The lock is released when the method exits, either through a normal return or an exception.  Exiting a synchronized method using a tail. call shall be implemented as though the tail. had not been specified.  noinlining specifies that the runtime shall not inline this method. Inlining refers to the process of replacing the call instruction with the body of the called method. This may be done by the runtime for optimization purposes.

14.4.4      Scope Blocks

Implementation Specific (Microsoft)

Scope blocks are syntactic sugar and primarily serve for readability and debugging purposes.

    <scopeBlock> ::= { <methodBodyItem>* }

A scope block defines the scope in which a local variable is accessible by its name. Scope blocks may be nested, such that a reference of a local variable will first be resolved in the innermost scope block, then at the next level, and so on until the top-most level of the method, is reached. A declaration in an inner scope block hides declarations in the outer layers.

If duplicate declarations are used, the reference will be resolved to the first occurrence. Even though valid CIL, duplicate declarations are not recommended.

Scoping does not affect the lifetime of a local variable. All local variables are created (and if specified initialized) when the method is entered. They stay alive until the execution of the method is completed.

The scoping does not affect the accessibility of a local variable by its zero based index. All local variables are accessible from anywhere within the method by their index.

The index is assigned to a local variable in the order of declaration. Scoping is ignored for indexing purposes. Thus, each local variable is assigned the next available index starting at the top of the method. This behavior can be altered by specifying an explicit index, as described by a <localsSignature> as shown in clause 14.4.1.3.

14.4.5      vararg Methods

vararg methods accept a variable number of arguments.  They shall use the vararg calling convention (see Section 14.3).

At each call site, a method reference shall be used to describe the types of the actual arguments that are passed.  The fixed part of the argument list shall be separated from the additional arguments with an ellipsis (see Partition I_alink=Partition_I).

The vararg arguments shall be accessed by obtaining a handle to the argument list using the CIL instruction arglist (see Partition III_alink=Partition_III). The handle may be used to create an instance of the value type System.ArgIterator which provides a typesafe mechanism for accessing the arguments (see Partition IV_alink=Partition_IV).

Example (informative):

The following example shows how a vararg method is declared and how the first vararg argument is accessed, assuming that at least one additional argument was passed to the method:

.method public static vararg void MyMethod(int32 required) {

  .maxstack 3

  .locals init (valuetype System.ArgIterator it, int32 x)

  ldloca it                         // initialize the iterator

  initobj valuetype System.ArgIterator

  ldloca it

  arglist                                  // obtain the argument handle

  call   instance void System.ArgIterator::.ctor(valuetype System.RuntimeArgumentHandle)   // call constructor of iterator

  /* argument value will be stored in x when retrieved, so load

   address of x */

  ldloca x

  ldloca it

  // retrieve the argument, the argument for required does not matter

  call   instance typedref System.ArgIterator::GetNextArg()

  call   object System.TypedReference::ToObject(typedref)       // retrieve the object

  castclass System.Int32            // cast and unbox

  unbox  int32

  cpobj  int32                      // copy the value into x

  // first vararg argument is stored in x

  ret

}

14.5      Unmanaged Methods

In addition to supporting managed code and managed data, the CLI provides facilities for accessing pre-existing native code from the underlying platform, known as unmanaged code.  These facilities are, by necessity, platform dependent and hence are only partially specified here. 

This standard specifies:

·              A mechanism in the file format for providing function pointers to managed code that can be called from unmanaged code (see clause 14.5.1).

·              A mechanism for marking certain method definitions as being implemented in unmanaged code (called platform invoke, see clause 14.5.2).

·              A mechanism for marking call sites used with method pointers to indicate that the call is to an unmanaged method (see clause 14.5.3).

·              A small set of pre-defined data types that can be passed (marshaled) using these mechanisms on all implementations of the CLI (see clause 14.5.5).  The set of types is extensible through the use of custom attributes and modifiers, but these extensions are platform-specific.

14.5.1      Method Transition Thunks

Note: This mechanism is not part of the Kernel Profile, so it may not be present in all conforming implementations of the CLI.  See Partition IV_alink=Partition_IV.

In order to call from unmanaged code into managed code some platforms require a specific transition sequence to be performed.  In addition, some platforms require that the representation of data types be converted (data marshalling).  Both of these problems are solved by the .vtfixup directive. This directive may appear several times only at the top level of a CIL assembly file, as shown by the following grammar:

<decl> ::=

Section

  .vtfixup <vtfixupDecl>

 

| …

5.10

 

The .vtfixup directive declares that at a certain memory location there is a table that contains metadata tokens referring to methods that shall be converted into method pointers. The CLI will do this conversion automatically when the file is loaded into memory for execution.  The declaration specifies the number of entries in the table, what kind of method pointer is required, the width of an entry in the table, and the location of the table:

<vtfixupDecl> ::=

  [ <int32> ] <vtfixupAttr>* at <dataLabel>

 

<vtfixupAttr> ::=

 fromunmanaged

| int32

| int64

 

The attributes int32 and int64 are mutually exclusive and int32 is the default. These attributes specify the width of each slot in the table. Each slot contains a 32-bit metadata token (zero-padded if the table has 64 bit slots), and the CLI converts it into a method pointer of the same width as the slot.

If fromunmanaged is specified, the CLI will generate a thunk that will convert the unmanaged method call to a managed call, call the method, and return the result to the unmanaged environment.  The thunk will also perform data marshalling in the platform-specific manner described for platform invoke.

The ilasm syntax does not specify a mechanism for creating the table of tokens, but a compiler may simply emit the tokens as byte literals into a block specified using the .data directive.

14.5.2      Platform Invoke

Methods defined in native code may be invoked using the platform invoke (also know as PInvoke or p/invoke) functionality of the CLI.  Platform invoke will switch from managed to unmanaged state and back and also handle necessary data marshalling. Methods that need to be called using PInvoke are marked as pinvokeimpl. In addition, the methods shall have the implementation attributes native and unmanaged (see clause 14.4.2.4). 

<methAttr> ::=

Description

Section

  pinvokeimpl ( <QSTRING> [as <QSTRING>] <pinvAttr>* )

Implemented in native code

 

| …

 

14.4.2

 

The first quoted string is a platform-specific description indicating where the implementation of the method is located (for example, on Microsoft Windows™ this would be the name of the DLL that implements the method).  The second (optional) string is the name of the method as it exists on that platform, since the platform may use name-mangling rules that force the name as it appears to a managed program to differ from the name as seen in the native implementation (this is common, for example, when the native code is generated by a C++ compiler).

Only static methods, defined at global scope (ie, outside of any type), may be marked pinvokeimpl. A method declared with pinvokeimpl shall not have a body specified as part of the definition.

<pinvAttr> ::=

Description (platform specific, suggestion only)

  ansi

ANSI character set.

| autochar

Determine character set automatically.

| cdecl

Standard C style call

| fastcall

C style fastcall.

| stdcall

Standard C++ style call.

| thiscall

The method accepts an implicit this pointer.

| unicode

Unicode character set.

| platformapi

Use call convention appropriate to target platform.

 

Implementation Specific (Microsoft)

In first release, platformapi is not recognized by ilasm.  Instead use winapi.

The attributes ansi, autochar, and unicode are mutually exclusive.  They govern how strings will be marshaled for calls to this method: ansi indicates that the native code will receive (and possibly return) a platform-specific representation that corresponds to a string encoded in the ANSI character set (typically this would match the representation of a C or C++ string constant); autochar indicates a platform-specific representation that is “natural” for the underlying platform; and unicode indicates a platform-specific representation that corresponds to a string encoded for use with Unicode methods on that platform.

The attributes cdecl, fastcall, stdcall, thiscall, and platformapi are mutually exclusive.  They are platform-specific and specificy the calling conventions for native code.

Implementation Specific (Microsoft)

In addition, the Microsoft implementation of the CLI on Microsoft Windows™ supports the following attributes:

lasterr to indicate that the native method supports C style last error querying.

nomangle to indicate that the name in the DLL should be used precisely as specified, rather than attempting to add A (for ascii) or W (widechar) to find platform-specific variants based on the type of string marshalling requested.

 

Example (informative):

The following shows the declaration of the method MessageBeep located in the Microsoft Windows™ DLL user32.dll:

.method public static pinvokeimpl("user32.dll" stdcall) int8 MessageBeep(unsigned int32) native unmanaged {}

14.5.3      Via Function Pointers

Unmanaged functions can also be called via function pointers. There is no difference between calling managed or unmanaged functions with pointers. However, the unmanaged function needs to be declared with pinvokeimpl as described in clause 14.5.2. Calling managed methods with function pointers is described in Section 13.5

14.5.4      COM Interop

Implementation Specific (Microsoft)

Unmanaged COM operates primarily by publishing uniquely identified interfaces and then sharing them between implementers (traditionally called “servers”) and users (traditionally called “clients”) of a given interface. It supports a rich set of types for use across the interface, and the interface itself can supply named constants and static methods, but it does not supply instance fields, instance methods, or virtual methods.

The CLI provides mechanisms useful to both implementers and users of existing classical COM interfaces. The goal is to permit programmers to deal with managed data types (thus eliminating the need for explicit memory management) while at the same time allowing interoperability with existing unmanaged servers and clients. COM Interop does not support the use of global functions (i.e. methods that are not part of a managed type), static functions, or parameterized constructors.

Given an existing classical COM interface definition as a type library, the tlbimp tool produces a file that contains the metadata describing that interface. The types it exposes in the metadata are managed counterparts of the unmanaged types in the original interface.

Implementers of an existing classical COM interface can import the metadata produced by tlbimp and then write managed types that provide the implementation of the methods required by that interface. The metadata specifies the use of managed data types in many places, and the CLI provides automatic marshaling (i.e. copying with reformatting) of data between the managed and unmanaged data types.

Implementers of a new service can simply write a managed program whose publicly visible types adhere to a simple set of rules. They can then run the tlbexp tool to produce a type library for classical COM users. This set of rules guarantees that the data types exposed to the classical COM user are unmanaged types that can be marshaled automatically by the CLI.

Implementers need to run the RegAsm tool to register their implementation with classical COM for location and activation purposes – if they wish to expose managed services to unmanaged code

Users of existing classical COM interfaces simply import the metadata produced by tlbimp. They can then reference the (managed) types defined there and the CLI uses the assembly mechanism and activation information to locate and instantiate instances of objects implementing the interface. Their code is the same whether the implementation of the interfaces is provided using classical COM (unmanaged) code or the CLI (managed) code: the interfaces they see use managed data types, and hence do not need explicit memory management.

For some existing classical COM interfaces, the CLI provides an implementation of the interface. In some cases the EE allows the user to specify all or parts of the implementation; for others it provides the entire implementation.

14.5.5      Data Type Marshaling

While data type marshaling is necessarily platform-dependent, this standard specifies a minimum set of data types that shall be supported by all conforming implementations of the CLI.  Additional data types may be supported in an implementation-dependent manner, using custom attributes and/or custom modifiers to specify any special handling required on the particular implementation.

The following data types shall be marshaled by all conforming implementations of the CLI; the native data type to which they conform is implementation specific:

·              All integer data types (int8, int16, unsigned int8, bool, char etc.) including the native integer types.

·              Enumerations, as their underlying data type.

·              All floating point data types (float32 and float64), if they are supported by the CLI implementation for managed code.

·              The type string.

·              Unmanaged pointers to any of the above types.

In addition, the following types shall be supported for marshaling from managed code to unmanaged code, but need not be supported in the reverse direction (i.e. as return types when calling unmanaged methods or as parameters when calling from unmanaged methods into managed methods)

·              One-dimensional zero-based arrays of any of the above

·              Delegates (the mechanism for calling from unmanaged code into a delegate is platform-specific; it should not be assumed that marshaling a delegate will produce a function pointer that can be used directly from unmanaged code)

Finally, the type GCHandle can be used to marshal an object to unmanaged code.  The unmanaged code receives a platform-specific data type that can be used as an “opaque handle” to a specific object.   See Partition IV_alink=Partition_IV.

14.5.6      Managed Native Calling Conventions (x86)

Implementation Specific (Microsoft)

This section is intended for an advanced audience. It describes the details of a native method call from managed code on the x86 architecture. The information provided in this section may be important for optimization purposes. This section is not important for the further understanding of the CLI and may be skipped.

There are two managed native calling conventions used on the x86. They are described here for completeness and because knowledge of these conventions allows an unsafe mechanism for bypassing the overhead of a managed to unmanaged code transition. 

14.5.6.1       Standard 80x86 Calling Convention

Implementation Specific (Microsoft)

The standard native calling convention is a variation on the fastcall convention used by VC. It differs primarily in the order in which arguments are pushed on the stack.

The only values that can be passed in registers are managed and unmanaged pointers, object references, and the built-in integer types int8, unsigned int8, int16, unsigned int16, int32, unsigned it32, native int and native unsigned  int. Enums are passed as their underlying type. All floating point values and 8-byte integer values are passed on the stack. When the return type is a value type that cannot be passed in a register, the caller shall create a buffer to hold the result and pass the address of this buffer as a hidden parameter.

Arguments are passed in left-to-right order, starting with the this pointer (for instance and virtual methods), followed by the return buffer pointer if needed, followed by the user-specified argument values.  The first of these that can be placed in a register is put into ECX, the next in EDX, and all subsequent ones are passed on the stack.

The return value is handled as follows:

Floating point values are returned on the top of the hardware FP stack.

Integers up to 32 bits long are returned in EAX.

64-bit integers are passed with EAX holding the least significant 32 bits and EDX holding the most significant 32 bits.

All other cases require the use of a return buffer, through which the value is returned.

In addition, there is a guarantee that if a return buffer is used a value is stored there only upon ordinary exit from the method. The buffer is not allowed to be used for temporary storage within the method and its contents will be unaltered if an exception occurs while executing the method.

Example (informative)

static System.Int32 f(int32 x)

The incoming argument (x) is placed in ECX; the return value is in EAX

static float64 f(int32 x, int32 y, int32 z)

x is passed in ECX, y in EDX, z on the top of stack; the return value is on the top of the floating point (FP) stack

static float64 f(int32 x, float64 y, float64 z)

x is passed in ECX, y on the top of the stack (not FP stack), z in EDX; the return value is on the top of the FP stack

virtual float64 f(int32 x, int64 y, int64 z)

this is passed in ECX, x in EDX, y pushed on the stack, then z pushed on the stack (hence z is top of the stack); the return value is on the top of the FP stack

virtual int64 f(int32 x, float64 y, float64 z)

this is passed in ECX, x in EDX, y pushed on the stack, then z pushed on the stack (hence z is on top of the stack); the return value is in EDX/EAX

virtual [mscorlib]System.Guid f(int32 x, float64 y, float64 z)

Since System.Guid is a value type the this pointer is passed in ECX, a pointer to the return buffer is passed in EDX, x is pushed, then y, and then z (hence z is on top the of stack); the return value is stored in the return buffer.

14.5.6.2       Varargs x86 Calling Convention

Implementation Specific (Microsoft)

All user-specified arguments are passed on the stack, pushed in left-to-right order.  Following the last argument (hence on top of the stack upon entry to the method body) a special cookie is passed which provides information about the types of the arguments that have been pushed.

As with the standard calling convention, the this pointer and a return buffer (if either is needed) are passed in ECX and/or EDX.

Values are returned in the same way as for the standard calling convention.

14.5.6.3       Fast Calls to Unmanaged Code

Implementation Specific (Microsoft)

Transitions from managed to unmanaged code require a small amount of overhead to allow exceptions and garbage collection to correctly determine the execution context. On an x86 processor, under the best circumstances, these transitions take approximately 5 instructions per call/return from managed to unmanaged code. In addition, any method that includes calls with transitions incurs an 8 instruction overhead spread across the calling method’s prolog and epilog.

This overhead can become a factor in performance of certain applications. For use in unverifiable code only, there is a mechanism to call from managed code to unmanaged code without the overhead of a transition. A “fast native call” is accomplished by the use of a calli instruction which indicates that the destination is managed even though the code address to which it refers is unmanaged. This can be arranged, for example, by initializing a variable of type function pointer in unmanaged code.

Clearly, this mechanism shall be tightly constrained since the transition is essential if there is any possibility of a garbage collection or exception occurring while in the unmanaged code. The following restrictions apply to the use of this mechanism:

The unmanaged code shall follow one of the two managed calling conventions (regular and vararg) that are specified below. In V1, only the regular calling convention is supported for fast native calls.

The unmanaged code shall not execute for any extended time, since garbage collection cannot begin while executing this code. It is wise to keep this under 100 instructions under all control flow paths.

The unmanaged code shall not throw an exception (managed or unmanaged), including access violations, etc. Page faults are not considered an exception for this purpose.

The unmanaged code shall not call back into managed code.

The unmanaged code shall not trigger a garbage collection (this usually follows from the restriction on calling back to managed code).

The unmanaged code shall not block. That is, it shall not call any OS-provided routine that might block the thread (synchronous I/O, explicitly acquiring locks, etc.)  Again, page faults are not a problem for this purpose.

The managed code that calls the unmanaged method shall not have a long, tight loop in which it makes the call. The total time for the loop to execute should remain under 100 instructions or the loop should include at least one call to a managed method. More technically, the method including the call shall produce “fully interruptible native code.” In future versions, there may be a way to indicate this as a requirement on a method.

Note: restrictions 2 through 6 apply not only to the unmanaged code called directly, but to anything it may call.

15      Defining and Referencing Fields

Fields are typed memory locations that store the data of a program.  The CLI allows the declaration of both instance and static fields. While static fields are associated with a type and shared across all instances of that type, instance fields are associated with a particular instance of that type.  When instantiated, the instance has its own copy of that field.

The CLI also supports global fields, which are fields declared outside of any type definition.  Global fields shall be static.

A field is defined by the .field directive:  (see Section 21.15)

<field> ::= .field <fieldDecl>

 

<fieldDecl> ::=

  [[ <int32> ]] <fieldAttr>* <type> <id> [= <fieldInit> | at <dataLabel>]

 

The <fieldDecl> has the following parts:

·              an optional integer specifying the byte offset of the field within an instance (see Section 9.7). If present, the type containing this field shall have the explicit layout attribute. An offset shall not be supplied for global or static fields.

·              any number of field attributes (see Section 15.2)

·              type

·              name

·              optionally either a <fieldInit> form or a data label

Global fields shall have a data label associated with them.  This specifies where, in the PE file, the data for that field is located.  Static fields of a type may, but do not need to, be assigned a data label.

Example (informative):

.field private class [.module Counter.dll]Counter counter

15.1      Attributes of Fields

Attributes of a field specify information about accessibility, contract information, interoperation attributes, as well as information on special handling.

The following subsections contain additional information on each group of predefined attributes of a field.

<fieldAttr> ::=

Description

Section

  assembly

Assembly accessibility.

15.1.1

| famandassem

Family and Assembly accessibility.

15.1.1

| family

Family accessibility.

15.1.1

| famorassem

Family or Assembly accessibility.

15.1.1

| initonly

Marks a constant field.

15.1.2

| literal

Specifies metadata field.  No memory is allocated at runtime for this field.

15.1.2

| marshal(<nativeType>)

Marshaling information.

15.1.3

| notserialized

Field is not serialized with other fields of the type.

15.1.2

| private

Private accessibility.

15.1.1

| compilercontrolled

Compiler controlled accessibility.

15.1.1

| public

Public accessibility.

15.1.1

| rtspecialname

Special treatment by runtime.

15.1.4

| specialname

Special name for other tools.

15.1.4

| static

Static field.

15.1.2

 

15.1.1      Accessibility Information

The accessibility attributes are assembly, famandassem, family, famorassem, private, compilercontrolled and public.  These attributes are mutually exclusive. 

Accessibility attributes are described in Section 8.2.

15.1.2      Field Contract Attributes

Field contract attributes are initonly, literal, static and notserialized.  These attributes may be combined. Only static fields may be literal.  The default is an instance field that may be serialized.

static specifies that the field is associated with the type itself rather than with an instance of the type. Static fields can be accessed without having an instance of a type, e.g. by static methods.  As a consequence, a static field is shared, within an application domain, between all instances of a type, and any modification of this field will affect all instances. If static is not specified, an instance field is created.

initonly marks fields which are constant after they are initialized. These fields may only be mutated inside a constructor. If the field is a static field, then it may be mutated only inside the type initializer of the type in which it was declared. If it is an instance field, then it may be mutated only in one of the instance constructors of the type in which it was defined. It may not be mutated in any other method or in any other constructor, including constructors of subclasses.

Note: The VES need not check whether initonly fields are mutated outside the constructors. The VES need not report any errors if a method changes the value of a constant. However, such code is not valid and is not verifiable.

Implementation Specific (Microsoft)

notserialized specifies that this field is not serialized when an instance of this type is serialized (see clause 9.1.6). It has no meaning on global or static fields, nor if the type  does not have the serializable attribute.

literal specifies that this field represents a constant value; they shall be assigned a value. In contrast to initonly fields, literal fields do not exist at runtime. There is no memory allocated for them. literal fields become part of the metadata but cannot be accessed by the code. literal fields are assigned a value by using the <fieldInit> syntax (see Section 15.2). 

Note: It is the responsibility of tools generating CIL to replace source code references to the literal with its actual value.  Hence changing the value of a literal requires recompilation of any code that references the literal.  Literal values are, thus, not version-resilient.

15.1.3      Interoperation Attributes

There is one attribute for interoperation with pre-existing native applications; it is platform-specific and shall not be used in code intended to run on multiple implementations of the CLI. The attribute is marshal and specifies that the field’s contents should be converted to and from a specified native data type when passed to unmanaged code.  Every conforming implementation of the CLI will have default marshaling rules as well as restrictions on what automatic conversions can be specified using the marshal attribute.  See also clause 14.5.5

Note: Marshaling of user-defined types is not required of all implementations of the CLI.  It is specified in this standard so that implementations which choose to provide it will allow control over its behavior in a consistent manner.  While this is not sufficient to guarantee portability of code that uses this feature, it does increase the likelihood that such code will be portable.

15.1.4      Other Attributes

The attribute rtspecialname indicates that the field name shall be treated in a special way by the runtime. 

Rationale: There are currently no field names that are required to be marked with rtspecialname.  It is provided for extensions, future standardization, and to increase consistency between the declaration of fields and methods (instance and type initializer methods shall be marked with this attribute).

The attribute specialname indicates that the field name has special meaning to tools other than the runtime, typically because it marks a name that has meaning for the Common Language Specification (CLS, see Partition I_alink=Partition_I).

15.2      Field Init Metadata

The <fieldInit> metadata can be optionally added to a field declaration. The use of this feature may not be combined with a data label.

The <fieldInit> information is stored in metadata and this information can be queried from metadata.  But the CLI does not use this information to automatically initialize the corresponding fields.  The field initializer is typically used with literal fields (see clause 15.1.2) or parameters with default values.   See Section 21.9

The following table lists the options for a field initializer. Note that while both the type and the field initializer are stored in metadata there is no requirement that they match.  (Any importing compiler is responsible for coercing the stored value to the target field type).  The description column in the table below provides additional information.

<fieldInit> ::=

Description

  bool ( true | false )

Boolean value, encoded as true or false

| bytearray ( <bytes> )

String of bytes, stored without conversion.  May be be padded with one zero byte to make the total byte-count an even number

| char ( <int32> )

16 bit unsigned integer (Unicode character)

| float32 ( <float64> )

32 bit floating point number, with the floating point number specified in parentheses.

| float32 ( <int32> )

<int32> is binary representation of float

| float64 ( <float64> )

64 bit floating point number, with the floating point number specified in parentheses.

| float64 ( <int64> )

<int64> is binary representation of double

| [ unsigned ] int8 ( <int8> )

8 bit integer with the integer specified in parentheses.

| [ unsigned ] int16 ( <int16> )

16 bit integer with the integer specified in parentheses.

| [ unsigned ] int32 ( <int32> )

32 bit integer with the integer specified in parentheses.

| [ unsigned ] int64 ( <int64> )

64 bit integer with the integer specified in parentheses.

| <QSTRING>

String. <QSTRING> is stored as Unicode

| nullref

Null object reference

 

Implementation Specific (Microsoft)

ilasm does not recognize the optional unsigned modifier before the int8, int16, int32 or int64 keywords

 

Example (informative):

The following example shows a typical use of this:

.field public static literal valuetype ErrorCodes no_error = int8(0)

The field named no_error is a literal of type ErrorCodes (a value type) for which no memory is allocated. Tools and compilers can look up the value and detect that it is intended to be an 8 bit signed integer whose value is 0.

15.3      Embedding Data in a PE File

There are several ways to declare a data field that is stored in a PE file. In all cases, the .data directive is used.

Data can be embedded in a PE file by using the .data directive at the top-level.

<decl> ::=

Section

  .data <datadecl>

 

| …

6.6

 

Data may also be declared as part of a type:

<classMember> ::=

Section

  .data <datadecl>

 

| …

9.2

 

Yet another alternative is to declare data inside a method:

<methodBodyItem> ::=

Section

  .data <datadecl>

 

| …

14.4.1

 

15.3.1      Data Declaration

A .data directive contains an optional data label and the body which defines the actual data. A data label shall be used if the data is to be accessed by the code.

<dataDecl> ::= [<dataLabel> =] <ddBody>

The body consists either of one data item or a list of data items in braces. A list of data items is similar to an array.

<ddBody> ::=

  <ddItem>

| { <ddItemList> }

 

A list of items consists of any number of items:

<ddItemList> ::= <ddItem> [, <ddItemList>]

 

The list may be used to declare multiple data items associated with one label. The items will be laid out in the order declared. The first data item is accessible directly through the label. To access the other items, pointer arithmetic is used, adding the size of each data item to get to the next one in the list. The use of pointer arithmetic will make the application not verifiable.  (Each data item shall have a <dataLabel> if it is to be referenced afterwards; missing a <dataLabel> is useful in order to insert alignment padding between data items)

A data item declares the type of the data and provides the data in parentheses. If a list of data items contains items of the same type and initial value, the grammar below can be used as a short cut for some of the types: the number of times the item shall be replicated is put in brackets after the declaration.

<ddItem> ::=

Description

  & ( <id> )

Address of label

| bytearray ( <bytes> )

Array of bytes

| char * ( <QSTRING> )

Array of (Unicode) characters

| float32 [( <float64> )] [[ <int32> ]]

32-bit floating point number, may be replicated

| float64 [( <float64> )] [[ <int32> ]]

64-bit floating point number, may be replicated

| int8 [( <int8> )] [[ <int32> ]]

8-bit integer, may be replicated

| int16 [( <int16> )] [[ <int32> ]]

16-bit integer, may be replicated

| int32 [( <int32> )] [[ <int32> ]]

32-bit integer, may be replicated

| int64 [( <int64> )] [[ <int32> ]]

64-bit integer, may be replicated

 

Example (informative):

The following declares a 32 bit signed integer with value 123:

.data theInt = int32(123)

The following declares 10 replications of an 8 bit unsigned integer with value 3:

.data theBytes = int8 (3) [10]

15.3.2      Accessing Data from the PE File

The data stored in a PE File using the .data directive can be accessed through a static variable, either global or a member of a type, declared at a particular position of the data:

<fieldDecl> ::= <fieldAttr>* <type> <id> at <dataLabel>

 

The data is then accessed by a program as it would access any other static variable, using instructions such as ldsfld, ldsflda, and so on (see Partition III_alink=Partition_III).

The ability to access data from within the PE File may be subject to platform-specific rules, typically related to section access permissions within the PE File format itself.

Example (informative):

The following accesses the data declared in the example of clause 15.3.1. First a static variable needs to be declared for the data, e.g. a global static variable:

.field public static int32 myInt at theInt

Then the static variable can be used to load the data:

ldsfld int32 myInt

// data on stack

15.3.3      Unmanaged Thread-local Storage

Implementation Specific (Microsoft)

Each PE file has a particular section whose initial contents are copied whenever a new thread is created. This section is called unmanaged thread local storage. The Microsoft implementation of ilasm allows the creation of this unmanaged thread local storage by extending the data declaration to include an option attribute, tls:

     <dataDecl> ::= [tls] [<dataLabel> =] <ddBody>

The CLI provides two mechanisms for dealing with thread-local storage (tls): an unmanaged mechanism and a managed mechanism. The unmanaged mechanism has a number of restrictions which are carried forward directly from the underlying platform into the CLI. For example, the amount of thread local storage is determined when the PE file is loaded and cannot be expanded. The amount is computed based on the static dependencies of the PE file, DLLs that are loaded as a program executes cannot create their own thread local storage through this mechanism. The managed mechanism, which does not have these restrictions, is part of the Base Class Library.

For unmanaged tls there is a particular native code sequence that can be used to locate the start of this section for the current thread. The CLI respects this mechanism. That is, when a reference is made to a static variable with a fixed RVA in the PE file and that RVA is in the thread-local section of the PE, the native code generated from the CIL will use the thread-local access sequence.

This has two important consequences:

A static variable with a specified RVA shall reside entirely in a single section of the PE file. The RVA specifies where the data begins and the type of the variable specifies how large the data area is.

When a new thread is created it is only the data from the PE file that is used to initialize the new copy of the variable. There is no opportunity to run the type initializer. For this reason it is probably wise to restrict the use of unmanaged thread local storage to the primitive numeric types and value types with explicit layout that have a fixed initial value and no type initializer.


15.4      Initialization of Non-Literal Static Data

This section and its subsections contain only informative text.

Many languages that support static data (i.e. variables that have a lifetime that is the entire program) provide for a means to initialize that data before the program begins running. There are three common mechanisms for doing this, and each is supported in the CLI.

15.4.1      Data Known at Link Time

When the correct value to be stored into the static data is known at the time the program is linked (or compiled for those languages with no linker step), the actual value can be stored directly into the PE file, typically into the data area (see Section 15.3). References to the variable are made directly to the location where this data has been placed in memory, using the OS supplied fix-up mechanism to adjust any references to this area if the file loads at an address other than the one assumed by the linker.

In the CLI, this technique can be used directly if the static variable has one of the primitive numeric types or is a value type with explicit type layout and no embedded references to managed objects. In this case the data is laid out in the data area as usual and the static variable is assigned a particular RVA (i.e. offset from the start of the PE file) by using a data label with the field declaration (using the at syntax). 

This mechanism, however, does not interact well with the CLI notion of an application domain (see Partition I_alink=Partition_I). An application domain is intended to isolate two applications running in the same OS process from one another by guaranteeing that they have no shared data. Since the PE file is shared across the entire process, any data accessed via this mechanism is visible to all application domains in the process, thus violating the application domain isolation boundary.

15.5      Data Known at Load Time

When the correct value is not known until the PE file is loaded (for example, if it contains values computed based on the load addresses of several PE files) it may be possible to supply arbitrary code to run as the PE file is loaded, but this mechanism is platform-specific and may not be available in all conforming implementations of the CLI.

Implementation Specific (Microsoft)

This mechanism, while available in the CLI, is strongly discouraged. The code runs under the process-wide loader lock, and the restrictions imposed by the underlying operating system make this a fragile mechanism.  The details are provided in clause 24.3.3.3.

15.5.1      Data Known at Run Time

When the correct value cannot be determined until type layout is computed, the user shall supply code as part of a type initializer to initialize the static data. The guarantees about type initialization are covered in clause 9.5.3.1. As will be explained below, global statics are modeled in the CLI as though they belonged to a type, so the same guarantees apply to both global and type statics.

Because the layout of managed types need not occur until a type is first referenced, it is not possible to statically initialize managed types by simply laying the data out in the PE file. Instead, there is a type initialization process that proceeds in the following steps:

1.             All static variables are zeroed.

2.             The user-supplied type initialization procedure, if any, is invoked as described in clause 9.5.3.

Within a type initialization procedure there are several techniques:

·              Generate explicit code that stores constants into the appropriate fields of the static variables. For small data structures this can be efficient, but it requires that the initializer be converted to native code, which may prove to be both a code space and an execution time problem.

·              Box value types. When the static variable is simply a boxed version of a primitive numeric type or a value type with explicit layout, introduce an additional static variable with known RVA that holds the unboxed instance and then simply use the box instruction to create the boxed copy.

·              Create a managed array from a static native array of data. This can be done by marshaling the native array to a managed array. The specific marshaler to be used depends on the native array. E.g., it may be a safearray.

·              Default initialize a managed array of a value type. The Base Class Library provides a method that zeroes the storage for every element of an array of unboxed value types (System.Runtime.CompilerServices.InitializeArray)

Implementation Specific (Microsoft)

Use Base Class Library deserialization. The Base Class Library provides serialization and deserialization services. These services can be found in the System.Runtime.Serialization namespace. An object can be converted to a serialized form, stored in the data section and accessed using a static variable with known RVA of type unsigned int8[]. The corresponding deserialization mechanism can then be used in the type initializer.

End informative text


16      Defining Properties

A Property is declared by the using the .property directive.  Properties may only be declared inside of types (ie global Properties are not supported)

<classMember> ::=

  .property <propHead> { <propMember>* }

 

See Section 21.31 and Section 21.32 for how Property information is stored in metadata.

<propHead> ::=

 [specialname][rtspecialname] <callConv> <type> <id> ( <parameters> )

 

The property directive specifies a calling convention (see Section 14.3), type, name, and parameter in parentheses. specialname marks the Property as special to other tools, while rtspecialname marks Property as special to the CLI.  The signature for the property (i.e., the <propHead> production) shall match the signature of the property's .get method (see below)

Rationale: There are currently no property names that are required to be marked with rtspecialname.  It is provided for extensions, future standardization, and to increase consistency between the declaration of properties and methods (instance and type initializer methods shall be marked with this attribute).

While the CLI places no constraints on the methods that make up a property, the CLS (see Partition I_alink=Partition_I) specifies a set of consistency constraints..

A property may contain any number of methods in its body.  The following table shows these and provides short descriptions of each item:

<propMember> ::=

Description

Section

| .custom <customDecl>

Custom attribute.

20

| .get <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Specifies the getter for the property.

 

| .other <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Specifies a method for the property other than the getter or setter.

 

| .set <callConv> <type>  [<typeSpec> ::] <methodName> ( <parameters> )

Specifies the setter for the property.

 

| <externSourceDecl>

.line or #line

5.7

 

.get specifies the getter for this property.  The <typeSpec> defaults to the current type.  Only one getter may be specified for a property.  To be CLS compliant, the definition of getter shall be marked specialname.

.set specifies the setter for this property.  The <typeSpec> defaults to the current type.  Only one setter may be specified for a property.  To be CLS compliant, the definition of setter shall be marked specialname.

.other is used to specify any other methods that this property comprises.

In addition, custom attributes (see Chapter 20) or source line declarations may be specified.

Example (informative):

This example shows the declaration of the property used in the example in Part 5.

.class public auto autochar MyCount extends [mscorlib]System.Object {

  .method virtual hidebysig public specialname instance int32 get_Count() {

         // body of getter

  }

  .method virtual hidebysig public specialname instance void set_Count(int32 newCount) {

         // body of setter

  }

  .method virtual hidebysig public instance void reset_Count() {

         // body of refresh method

  }

  // the declaration of the property

  .property int32 Count() {

         .get instance int32 get_Count()

         .set instance void set_Count(int32)

         .other instance void reset_Count()

  }

}

17      Defining Events

Events are declared inside types with the .event directive; there are no global events.

<classMember> ::=

Section

  .event <eventHead> { <eventMember>* }

 

| …

9

 

See Section 21.13 and Section 21.11

<eventHead> ::=

  [specialname] [rtspecialname] [<typeSpec>] <id>

 

In typical usage, the <typeSpec> (if present) identifies a delegate whose signature matches the arguments passed to the event’s fire method.

The event head may contain the keywords specialname or rtspecialname. specialname marks the name of the property for other tools, while rtspecialname marks the name of the event as special for the runtime.

Rationale: There are currently no event names that are required to be marked with rtspecialname.  It is provided for extensions, future standardization, and to increase consistency between the declaration of events and methods (instance and type initializer methods shall be marked with this attribute).

<eventMember> ::=

Description

Section

 

  .addon <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Add method for event.

 

| .custom <customDecl>

Custom attribute.

20

| .fire <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Fire method for event.

 

| .other <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Other method.

 

| .removeon <callConv> <type> [<typeSpec> ::] <methodName> ( <parameters> )

Remove method for event.

 

| <externSourceDecl>

.line or #line

5.7

 

The .addon directive specifies the add method , and the <typeSpec> defaults to the same type as the event.  The CLS specifies naming conventions and consistency constraints for events, and requires that the definition of the add method be marked with specialname.

The .removeon directive specifies the remove method , and the <typeSpec> defaults to the same type as the event.  The CLS specifies naming conventions and consistency constraints for events, and requires that the definition of the remove method be marked with specialname.

The .fire directive specifies the fire method , and the <typeSpec> defaults to the same type as the event.  The CLS specifies naming conventions and consistency constraints for events, and requires that the definition of the fire method be marked with specialname.

An event may contain any number of other methods specified with the .other directive. From the point of view of the CLI, these methods are only associated with each other through the event. If they have special semantics, this needs to be documented by the implementer.

Events may also have custom attributes (Chapter 20) associated with them and they may declare source line information.

Example (informative):

This shows the declaration of an event, its corresponding delegate, and typical implementations of the add, remove, and fire method of the event. The event and the methods are declared in a class called Counter.

// the delegate

.class private sealed auto autochar TimeUpEventHandler extends [mscorlib]System.MulticastDelegate {

  .method public hidebysig specialname rtspecialname instance void .ctor(object 'object', native int 'method') runtime managed {}

  .method public hidebysig virtual instance void Invoke() runtime managed {}

  .method public hidebysig newslot virtual instance class [mscorlib]System.IAsyncResult BeginInvoke(class [mscorlib]System.AsyncCallback callback, object 'object') runtime managed {}

  .method public hidebysig newslot virtual instance void EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed {}

}

 

// the class that declares the event

.class public auto autochar Counter extends [mscorlib]System.Object {

 

// field to store the handlers, initialized to null

.field private class TimeUpEventHandler timeUpEventHandler

 

// the event declaration

.event TimeUpEventHandler startStopEvent {

  .addon instance void add_TimeUp(class TimeUpEventHandler 'handler')

  .removeon instance void remove_TimeUp(class TimeUpEventHandler 'handler')

  .fire instance void fire_TimeUpEvent()

}

 

// the add method, combines the handler with existing delegates

.method public hidebysig virtual specialname instance void add_TimeUp(class TimeUpEventHandler 'handler') {

  .maxstack 4

  ldarg.0

  dup

  ldfld  class TimeUpEventHandler Counter::TimeUpEventHandler

  ldarg  'handler'

  call   class[mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

  castclass TimeUpEventHandler

  stfld  class TimeUpEventHandler Counter::timeUpEventHandler

  ret

}

 

// the remove method, removes the handler from the multicast delegate

.method virtual public specialname void remove_TimeUp(class TimeUpEventHandler 'handler') {

  .maxstack 4

  ldarg.0

  dup

  ldfld  class TimeUpEventHandler Counter::timeUpEventHandler

  ldarg  'handler'

  call   class[mscorlib]System.Delegate [mscorlib]System.Delegate::Remove(class [mscorlib]System.Delegate, class [mscorlib]System.Delegate)

  castclass TimeUpEventHandler

  stfld  class TimeUpEventHandler Counter::timeUpEventHandler

  ret

}

 

// the fire method

.method virtual family specialname void fire_TimeUpEvent() {

  .maxstack 3

  ldarg.0

  ldfld  class TimeUpEventHandler Counter::timeUpEventHandler

  callvirt instance void TimeUpEventHandler::Invoke()

  ret

}

} // end of class Counter

18      Exception Handling

In the CLI, a method may define a range of CIL instructions that are said to be protected.  This is called the try block.  It can then associate one or more handlers with that try block.  If an exception occurs during execution anywhere within the try block, an exception object is created that describes the problem.  The CIL then takes over, transferring control from the point at which the exception was thrown, to the block of code that is willing to handle that exception.  See Partition I_alink=Partition_I.

<sehBlock> ::=

  <tryBlock> <sehClause> [<sehClause>*]

 

The next few sections expand upon this simple description, by describing the five kinds of code block that take part in exception processing: try, catch, filter, finally, and fault.   (note that there are restrictions upon how many, and what kinds of <sehClause> a given <tryBlock> may have; see Partition I_alink=Partition_I. for details.

The remaining syntax items are described in detail below; they are collected here for reference.

<tryBlock> ::=

Description

.try <label> to <label>

Protect region from first label to prior to second

| .try <scopeBlock>

<scopeBlock> is protected

 

<sehClause> ::=

Description

  catch <typeReference> <handlerBlock>

Catch all objects of the specified type

| fault <handlerBlock>

Handle all exceptions but not normal exit

| filter <label> <handlerBlock>

Enter handler only if filter succeeds

| finally <handlerBlock>

Handle all exceptions and normal exit

 

<handlerBlock> ::=

Description

handler <label> to <label>

Handler range is from first label to prior to second

| <scopeBlock>

<scopeBlock> is the handler block

 

18.1      Protected Blocks

A try, or protected, or guarded, block is declared with the .try directive. 

<tryBlock> ::=

Descriptions

.try <label> to <label>

Protect region from first label to prior to second.

| .try <scopeBlock>

<scopeBlock> is protected

 

In the first, the protected block is delimited by two labels. The first label is the first instruction to be protected, while the second label is the instruction just beyond the last one to be protected.  Both labels shall be defined prior to this point.

The second uses a scope block (see clause 14.4.4) after the .try directive – the instructions within that scope are the ones to be protected.

18.2      Handler Blocks

<handlerBlock> ::=

Description

| handler <label> to <label>

Handler range is from first label to prior to second

| <scopeBlock>

<scopeBlock> is the handler block

 

In the first syntax, the labels enclose the instructions of the handler block, the first label being the first instruction of the handler while the second is the instruction immediately after the handler. Alternatively, the handler block is just a scope block.

Implementation Specific (Microsoft)

ilasm requires labels used to specify any exceptions blocks to be defined beforehand in the source. ilasm supports the following additional syntax for use in round-tripping:

<handlerBlock> ::= handler <int32> to <int32>

18.3      Catch

A catch block is declared using the catch keyword.  This specifies the type of exception object the clause is designed to handle, and the handler code itself.

<sehClause> ::=

  catch <typeReference> <handlerBlock>

 

Example (informative):

.try {

  ...                        // protected instructions

  leave  exitSEH                    // normal exit

} catch [mscorlib]System.FormatException {

  ...                        // handle the exception

  pop                        // pop the exception object

  leave  exitSEH                    // leave catch handler

}

exitSEH:                      // continue here

18.4      Filter

A filter block is declared using the filter keyword.

<sehClause> ::= …

| filter <label> <handlerBlock>

| filter <scope> <handlerBlock>

 

The filter code begins at the specified label and ends at the first instruction of the handler block.  (Note that the CLI demands that the filter block shall immediately precede, within the CIL stream, its corresponding handler block)

Example (informative):

.method public static void m () {

    .try {

      ...                     // protected instructions

      leave     exitSEH       // normal exit

    }

    filter {

      ...                     // decide whether to handle

      pop                     // pop exception object

      ldc.i4.1         // EXCEPTION_EXECUTE_HANDLER

      endfilter        // return answer to CLI

    }

    {

      ...                     // handle the exception

      pop                     // pop the exception object

      leave     exitSEH       // leave filter handler

    }

exitSEH:

    ...

}

18.5      Finally

A finally block is declared using the finally keyword.  This specifies the handler code, with this grammar:

<sehClause> ::= …

| finally <handlerBlock>

 

The last possible CIL instruction that can be executed in a finally handler shall be endfinally.

Example (informative):

.try {

  ...                        // protected instructions

  leave exitTry        // shall use leave

} finally {

  ...                        // finally handler

  endfinally                

}

exitTry:               // back to normal

18.6      Fault Handler

A fault block is declared using the fault keyword.  This specifies the handler code, with this grammar:

<sehClause> ::= …

| fault <handlerBlock>

 

The last possible CIL instruction that can be executed  in a fault handler shall be endfault.

Example (informative):

.method public static void m() {

startTry:

         ...                  // protected instructions

         leave  exitSEH       // shall use leave

endTry:

 

startFault:

         ...                  // fault handler instructions

         endfault            

endFault:

 

  .try startTry to endTry fault handler startFault to endFault

 

exitSEH:                      // back to normal

}

19      Declarative Security

Many languages that target the CLI use attribute syntax to attach declarative security attributes to items in the metadata. This information is actually converted by the compiler into an XML-based representation that is stored in the metadata, see Section 21.11.  By contrast, ilasm requires the conversion information to be represented in its input.

<securityDecl> ::=

  .permissionset <secAction> = ( <bytes> )

| .permission <secAction> <typeReference> ( <nameValPairs> )

 

In .permission, <typeReference> specifies the permission class and <nameValPairs> specifies the settings.   See Section 21.11

In .permissionset the bytes specify the serialized version of the security settings:

<secAction> ::=

Description

  assert

Assert permission so that callers  do not need it.

| demand

Demand permission of all callers.

| deny

Deny permission so checks will fail.

| inheritcheck

Demand permission of a subclass.

| linkcheck

Demand permission of caller.

| permitonly

Reduce permissions so check will fail.

| reqopt

Request optional additional permissions.

| reqrefuse

Refuse to be granted these permissions.

| request

Hint that permission may be required.

 

Implementation Specific (Microsoft)

The following security action is Microsoft-specific.  A conforming implementation of the CLI may ignore this security action if present in an assembly

Implementation Specific (Microsoft)

<secAction> ::=

Description

| prejitgrant

Persisted denied set at prejit time.

 

<nameValPairs> ::= <nameValPair> [, <nameValPair>]*

 

<nameValPair> ::= <SQSTRING> = <SQSTRING>

20      Custom Attributes

Custom attributes add user-defined annotations to the metadata. Custom attributes allow an instance of a type to be stored with any element of the metadata. This mechanism can be used to store application specific information at compile time and access it either at runtime or when another tool reads the metadata. While any user-defined type can be used as an attribute, CLS compliance requires that attributes will be instances of types whose parent is System.Attribute. The CLI predefines some attribute types and uses them to control runtime behavior. Some languages predefine attribute types to represent language features not directly represented in the CTS. Users or other tools are welcome to define and use additional attribute types.

Custom attributes are declared using the directive .custom. Followed by this directive is the method declaration for a type constructor, optionally followed by a <bytes> in parentheses:

<customDecl> ::=

  <ctor> [ = ( <bytes> ) ]

 

The <ctor> item represents a method declaration (see Section 14.4), specific for the case where the method's name is .ctor.

For example:

.custom instance void myAttribute::.ctor(bool, bool) = ( 01 00 00 01 00 00 )

Custom attributes can be attached to any item in metadata, except a custom attribute itself.  Commonly, custom attributes are attached to assemblies, modules, classes, interfaces, value types, methods, fields, properties and events  (the custom attribute is attached to the immediately preceding declaration)

The <bytes> item is not required if the constructor takes no arguments.  In these cases, all that matters is the presence of the custom attribute.

If the constructor takes parameters, their values shall be specified in the <bytes> item.  The format for this ‘blob’ is defined in Section 22.3.

Example (informative):

The following example shows a class that is marked with the System.SerializableAttribute and a method that is marked with the System.Runtime.Remoting.OneWayAttribute. The keyword serializable corresponds to the System.SerializableAttribute.

.class public MyClass {

  .custom void [mscorlib]System.SerializableAttribute::.ctor ()

  .method public static void main() {

         .custom void [mscorlib]System.Runtime.Remoting.OneWayAttribute::.ctor ()

         ret

  }

}

20.1      CLS Conventions: Custom Attribute Usage

CLS imposes certain conventions upon the use of Custom Attributes in order to improve cross-language operation.  See Partition I_alink=Partition_I for details.

20.2      Attributes Used by the CLI

There are two kinds of Custom Attributes, called (genuine) Custom Attributes, and Pseudo Custom Attributes. Custom Attributes and Pseudo Custom Attributes are treated differently, at the time they are defined, as follows:

·              A Custom Attribute is stored directly into the metadata; the‘blob’ which holds its defining data is stored as-is. That ‘blob’ can be retrieved later.

·              A Pseudo Custom Attribute is recognized because its name is one of a short list.  Rather than store its ‘blob’ directly in metadata, that ‘blob’ is parsed, and the information it contains is used to set bits and/or fields within metadata tables.  The ‘blob’ is then discarded; it cannot be retrieved later.

Pseudo Custom Attributes therefore serve to capture user directives, using the same familiar syntax the compiler provides for regular Custom Attributes, but these user directives are then stored into the more space-efficient form of metadata tables. Tables are also faster to check at runtime than (genuine) Custom Attributes.

Many Custom Attributes are invented by higher layers of software. They are stored and returned by the CLI, without its knowing or caring what they ‘mean’.  But all Pseudo Custom Attributes, plus a collection of regular Custom Attributes, are of special interest to compilers and to the CLI.  An example of such Custom Attributes is System.Reflection.DefaultMemberAttribute. This is stored in metadata as a regular Custom Attribute ‘blob’, but reflection uses this Custom Attribute when called to invoke the default member (property) for a type.

The following subsections list all of the Pseudo Custom Attributes and distinguished Custom Attributes, where distinguished means that the CLI and/or compilers pay direct attention to them, and their behavior is affected in some way.

In order to prevent name collisions into the future, all custom attributes in the System namespace are reserved for standardization.

20.2.1      Pseudo Custom Attributes

The following table lists the CLI Pseudo Custom Attributes.  They are defined in either the System or the System.Reflection namespaces. 

Attribute

Description

AssemblyAlgorithmIDAttribute

Records the ID of the hash algorithm used (reserved only)

AssemblyFlagsAttribute

Records the flags for this assembly (reserved only)

DllImportAttribute

Provides information about code implemented within an unmanaged library

FieldOffsetAttribute

Specifies the byte offset of fields within their enclosing class or value type

InAttribute

Indicates that a method parameter is an [in] argument

MarshalAsAttribute

Specifies how a data item should be marshalled between managed and unmanaged code -- see Section 22.4.

MethodImplAttribute

Specifies details of how a method is implemented

OutAttribute

Indicates that a method parameter is an [out] argument

StructLayoutAttribute

Allows the caller to control how the fields of a class or value type are laid out in managed memory

 

Not all of these Pseudo Custom Attributes are specified in this standard, but all of them are reserved and shall not be used for other purposes.  For details on these attributes, see the documentation for the corresponding class in Partition IV_alink=Partition_IV.

The Pseudo Custom Attributes above affect bits and fields in metadata, as follows:

AssemblyAlgorithmIDAttribute : sets the Assembly.HashAlgId field

AssemblyFlagsAttribute : sets the Assembly.Flags field

DllImportAttribute : sets the Method.Flags.PinvokeImpl bit for the attributed method; also, adds a new row into the ImplMap table (setting MappingFlags, MemberForwarded, ImportName and ImportScope columns)

FieldOffsetAttribute : sets the FieldLayout.OffSet value for the attributed field

InAttribute : sets the Param.Flags.In bit for the attributed parameter

MarshalAsAttribute : sets the Field.Flags.HasFieldMarshal bit for the attributed field (or the Param.Flags.HasFieldMarshal bit for the attributed parameter); also enters a new row into the FieldMarshal table for both Parent and NativeType columns.  

MethodImplAttribute : sets the Method.ImplFlags field of the attributed method

OutAttribute : sets the Param.Flags.Out bit for the attributed parameter

StructLayoutAttribute : sets the TypeDef.Flags.LayoutMask sub-field for the attributed type. And, optionally, the TypeDef.Flags.StringFormatMask sub-field, the ClassLayout.PackingSiz ,and ClassLayout.ClassSize fields for that type.

Implementation Specific (Microsoft)

Use of the following Pseudo Custom Attributes renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access the metadata items set by those Custom Attributes.

Implementation Specific (Microsoft)

Attribute

Description

ComImportAttribute

Provides information about native code reached as a COM component

OptionalAttribute

Marks a method parameter as optional

NonSerializedAttribute

Indicates that a field should not be serialized

PreserveSigAttribute

Specifies HRESULT or retval signature transformation

SerializableAttribute

Indicates that a type can be serialized

 

Implementation Specific (Microsoft)

The Pseudo Custom Attributes above affect bits and fields in metadata, as follows:

ComImportAttribute : sets the TypeDef.Flags.Import bit for the attributed type

OptionalAttribute : sets the Param.Flags.Optional bit for the attributed parameter

NonSerializedAttribute : sets the Field.Flags.NotSerialized bit for the attributed field

PreserveSigAttribute : sets the Method.ImplFlags.PreserveSig bit of the attributed method

SerializableAttribute : sets the TypeDef.Flags.Serializable bit for the attributed type

20.2.2      Custom Attributes Defined by the CLS

The CLS specifies certain Custom Attributes and requires that conformant languages support them. These attributes are located under System.

Attribute

Description

AttributeUsageAttribute

Used to specify how an attribute is intended to be used.

ObsoleteAttribute

Indicates that an element is not to be used.

CLSCompliantAttribute

Indicates whether or not an element is declared to be CLS compliant through an instance field on the attribute object.

 

20.2.3      Custom Attributes for CIL-to-Native-Code Compiler and Debugger

Implementation Specific (Microsoft)

The following Custom Attributes that control the runtime behavior of a CIL-to-native-code compiler and a runtime debugger are defined in the System.Diagnostics namespace.  Their use renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those Custom Attributes.

Implementation Specific (Microsoft)

Attribute

Description

DebuggableAttribute

Controls a CIL-to-native-code compiler to produce code that is easier to debug

DebuggerHiddenAttribute

Specifies a debugger should step over the attributed method or property

DebuggerStepThroughAttribute

Specifies a debugger should step through the attributed method or property (it may step into a method called by this one)

 

20.2.4      Custom Attributes for Remoting

Implementation Specific (Microsoft)

The following Custom Attributes are used to control the behavior of remoting.  They are defined in the System.Runtime.Remoting namespace.  Their use renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those custom attributes.

Implementation Specific (Microsoft)

Attribute

Description

ContextAttribute

Root for all context attributes.

OneWayAttribute

Marks a method as “fire and forget”

SynchronizationAttribute

Specifies the synchronization options for a class

ThreadAffinityAttribute

Refinement of Synchronized Context.

 

20.2.5      Custom Attributes for Security

The following Custom Attributes affect the security checks performed upon method invocations at runtime.  They are defined in the System.Security namespace.

Attribute

Description

DynamicSecurityMethodAttribute

Indicates to the CLI that the method requires space to be allocated for a security object

SuppressUnmanagedCodeSecurityAttribute

Indicates the target method, implemented as unmanaged code, should skip per-call checks

 

The following Custom Attributes are defined in the System.Security.Permissions. namespace.   Note that these are all base classes; the actual instances of security attributes found in assemblies will be sub-classes of these.

Attribute

Description

CodeAccessSecurityAttribute

This is the base attribute class for declarative security using custom attributes.

DnsPermissionAttribute

Custom attribute class for declarative security with DnsPermission

EnvironmentPermissionAttribute

Custom attribute class for declarative security with EnvironmentPermission.

FileIOPermissionAttribute

Custom attribute class for declarative security with FileIOPermission.

ReflectionPermissionAttribute

Custom attribute class for declarative security with ReflectionPermission.

SecurityAttribute

This is the base attribute class for declarative security from which CodeAccessSecurityAttribute is derived.

SecurityPermissionAttribute

Indicates whether the attributed method can affect security settings

SiteIdentityPermissionAttribute

Custom attribute class for declarative security with SiteIdentityPermission.

SocketPermissionAttribute

Custom attribute class for declarative security with SocketPermission.

StrongNameIdentityPermissionAttribute

Custom attribute class for declarative security with StrongNameIdentityPermission.

WebPermissionAttribute

Custom attribute class for declarative security with WebPermission.

 

Note that any other security-related Custom Attributes (ie, any Custom Attributes that derive from System.Security.Permissions.SecurityAttribute) included into an assembly, may cause a conforming implementaion of the CLI to reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those security-related Custom Attributes.  (This statement in fact holds true for any Custom Attributes that cannot be resolved; security-related Custom Attributes are just one particular case)

Implementation Specific (Microsoft)

The following security-related Custom Attributes are defined in the System.Security.Permissions namespace.  Their use renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those Custom Attributes.

Implementation Specific (Microsoft)

Attribute

Description

RegistryPermissionAttribute

Indicates whether the attributed method can access the Registry

UIPermissionAttribute

Custom attribute class for declarative security with UIPermission.

ZoneIdentityPermissionAttribute

Custom attribute class for declarative security with ZoneIdentityPermission.

 

20.2.6      Custom Attributes for TLS

A Custom Attribute that denotes a TLS (thread-local storage, see clause 15.3.3) field is defined in the System. namespace

Attribute

Description

ThreadStaticAttribute

Provides for type member fields that are relative for the thread.

 

20.2.7      Pseudo Custom Attributes for the Assembly Linker

Implementation Specific (Microsoft)

The following Pseudo Custom Attributes are used by the al tool to transfer information between modules and assemblies (they are temporarily attached to a TypeRef to a class called AssemblyAttributesGoHere) then merged by al and attached to the assembly. These attributes are defined in the System.Runtime.CompilerServices namespace.  Their use renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those Pseudo Custom Attributes.

Implementation Specific (Microsoft)

Attribute

Description

AssemblyCultureAttribute

Specifies which culture an assembly supports

AssemblyVersionAttribute

String holding version of assembly (in the format major.minor.build.revision)

 

Implementation Specific (Microsoft)

The Pseudo Custom Attributes above affect bits and fields in metadata, as follows:

AssemblyCulture : sets the Assembly.Culture field

AssemblyVersion : sets the Assembly.MajorVersion, MinorVersion, BuildNumber and RevisionNumber

20.2.8      Custom Attributes Provided for Interoperation with Unmanaged Code

Implementation Specific (Microsoft)

The following Custom Attributes are used to control the interoperation with COM 1.x and classical COM. These attributes are located under System.Runtime.InteropServices.  More information can also be found in the Partition IV_alink=Partition_IV. Their use renders the assembly that contains them non-portable; a conforming implementation of the CLI may reject such an assembly when it is loaded, or throw an exception at runtime if any attempt is made to access those Custom Attributes.

Implementation Specific (Microsoft)

Attribute

Description

ClassInterfaceAttribute

Specifies how the class is exported to COM (as DispInterface, as a Dual Interface, or not at all)

ComAliasNameAttribute

Applied to a parameter or field to indicate the COM alias for the parameter or field type.

ComConversionLossAttribute

Indicates that information was lost about a class or interface when it was imported from a type library to an assembly

ComEmulateAttribute

Used on a type to indicate that it is an emulator type for a different type.

ComRegisterFunctionAttribute

Used on a method to indicate that the method should be called when the assembly is registered for use from COM.

ComSourceInterfacesAttribute

Identifies the list of interfaces that are sources of events for the type.

ComUnregisterFunctionAttribute

Used on a method to indicate that the method should be called when the assembly is unregistered for use from COM.

ComVisibleAttribute

Can be applied to an individual type or to an entire assembly to control COM visibility.

DispIdAttribute

Custom attribute to specify the COM DISPID of a Method or Field.

GuidAttribute

Used to supply the GUID of a type, interface or an entire type library.

HasDefaultInterfaceAttribute

Used to specify that a class has a COM default interface.

IdispatchImplAttribute

Indicates which IDispatch implementation the CLI uses when exposing dual interfaces and dispinterfaces to COM

ImportedFromTypeLibAttribute

Custom attribute to specify that a module is imported from a COM type library.

InterfaceTypeAttribute

Indicates whether a managed interface is dual, IDispatch or IUnknown when exposed to COM

NoComRegistrationAttribute

Used to indicate that an otherwise public, COM-creatable type should not be registered for use form COM applications.

NoIDispatchAttribute

This attribute is used to control how the class responds to queries for an IDispatch Interface.

ProgIdAttribute

Custom attribute that allows the user to specify the prog ID of a class.

TypeLibFuncAttribute

Contains the FUNCFLAGS that were originally imported for this function from the COM type library.

TypeLibTypeAttribute

Contains the TYPEFLAGS that were originally imported for this type from the COM type library.

TypeLibVarAttribute

Contains the VARFLAGS that were originally imported for this variable from the COM type library.

 

20.2.9      Custom Attributes, Various

The following Custom Attributes control various aspects of the CLI:

Attribute

Description

ConditionalAttribute

Used to mark methods as callable, based on some compile-time condition.  If the condition is false, the method will not be called

DecimalConstantAttribute

Stores the value of a decimal constant in metadata

DefaultMemberAttribute

Defines the member of a type that is the default member used by reflection’s InvokeMember.

FlagsAttribute

Custom attribute indicating an enumeration should be treated as a bitfield; that is, a set of flags

IndexerNameAttribute

Indicates the name by which an indexer will be known in programming languages that do not support indexers directly

ParamArrayAttribute

Indicates that the method will allow a variable number of arguments in its invocation

 

21      Metadata Logical Format: Tables

This section defines the structures that describe metadata, and how they are cross-indexed.  This corresponds to how metadata is laid out, after being read into memory from a PE file.  (For a description of metadata layout inside the PE file itself, see Chapter 23)

Metadata is stored in two kinds of structure – tables (arrays of records), and heaps.  There are four heaps in any module: String, Blob, Userstring and Guid.  The first three are byte arrays (so valid indexes into these heaps might be 0, 23, 25, 39, etc).  The Guid heap is an array of GUIDs, each 16 bytes wide.  Its first element is numbered 1, its second 2, and so on.

Each entry in each column of each table is either a constant or an index.

Constants are either literal values (eg ALG_SID_SHA1 = 4, stored in the HashAlgId column of the Assembly table), or, more commonly, bitmasks.  Most bitmasks (they are almost all called “Flags”) are 2 bytes wide (eg the Flags column in the Field table), but there are a few that are 4 bytes (eg the Flags column in the TypeDef table)

Each index is either 2 bytes wide, or 4 bytes wide.  The index points into another (or the same) table, or into one of the four heaps.  The size of each index column in a table is only made 4 bytes if it needs to be, for that particular module.   So, if a particular column indexes a table, or tables, whose highest row number fits in a 2-byte value, the indexer column need only be 2 bytes wide.  Conversely, for huge tables, containing 64K rows or more, an indexer of that table will be 4 bytes wide.

Note that indexes begin at 1, meaning the first row in any given metadata table.  An index value of zero denotes that it does not index a row at all (it behaves like a null reference)

The columns that index a metadata table are of two sorts:

·              Simple – that column indexes one, and only one, table.  e.g., the FieldList column in the TypeDef table always indexes the Field table.  So all values in that column are simple integers, giving the row number in the target table

·              Coded – that column indexes any of several tables. e.g., the Extends column in the TypeDef table can index into the TypeDef table, or into the TypeRef table.   A few bits of that index value are reserved to define which table it targets.  For the most part, this specification talks of index values after being decoded into row numbers within the target table. However, the specification includes a description of these coded indexes in the section that describes the physical layout of Metadata (Chapter 23).

Metadata preserves name strings, as created by a compiler or code generator, unchanged.  Essentially it treats each string as an opaque 'blob'. In particular, it preserves case.  The CLI imposes no limit on the size of names stored in metadata and subsequently processed by the CLI

Implementation Specific (Microsoft)

For first release, strings are limited in length.  Depending on its purpose, a string  can be no larger than MAX_CLASS_NAME (defined as 1024) or MAX_PATH_NAME (defined as 260).  These values refer to the maximum number of bytes that the string, after being converted into UTF8 format, may occupy; that includes a terminating null character.  It is intended that this limitation be removed in a future release.  Within this document, the above restrictions are abbreviated to the phrase: “… is limited to MAX_CLASS_NAME” or “… is limited to MAX_PATH_NAME

Matching AssemblyRefs and ModuleRefs to their corresponding Assembly and Module shall be performed case-blind (see Partition I_alink=Partition_I). However, all other name matches (type, field, method, property, event) is exact – so that this level of resolution is the same across all platforms, whether their OS is case-sensitive or not.

Tables are given both a name (eg "Assembly") and numbered (eg 0x20).  The number for each table is listed immediately with its title in the following sections.

A few of the tables represent extensions to regular CLI files. Specifically, ENCLog and ENCMap, which  occur in temporary images, generated during "Edit and Continue" or "incremental compilation" scenarios, whilst debugging.  Both table types are reserved for future use.

References to the methods or fields of a Type are stored together in a metadata table called the MemberRef table.  However, sometimes, for clearer explanation,  this specification distinguishes between  these two kinds of reference, calling them “MethodRef” and “FieldRef”.


This contains informative text only

21.1      Metadata Validation Rules

The sections that follow describe the schema for each kind of metadata table, and explain the detailed rules that guarantee metadata emitted into any PE file is valid.  Checking that metadata is valid ensures that later processing - checking the CIL instruction stream for type safety, building method tables, CIL-to-native-code compilation, data marshalling, etc will not cause the CLI to crash or behave in an insecure fashion.

In addition, some of the rules are used to check compliance with the CLS requirements (see Partition I_alink=Partition_I) even though these are not related to valid Metadata.  These are marked with a trailing [CLS] tag.

The rules for valid metadata refer to an individual module.  A module is any collection of metadata that could typically be saved to a disk file. This includes the output of compilers and linkers, or the output of script compilers (where often the metadata is held only in memory, but never actually saved to a file on disk).

The rules address intra-module validation only.  So, validator software, for example, that checks conformance with this spec, need not resolve references or walk type hierarchies defined in other modules. However, it should be clear that even if two modules, A and B, analyzed separately, contain only valid metadata, they may still be in error when viewed together (e.g., a call from Module A, to a method defined in module B, might specify a callsite signature that does not match the signatures defined for that method in B)

All checks are categorized as ERROR, WARNING or CLS.

·              An ERROR reports something that might cause a CLI to crash or hang, it might run but produce wrong answers; or it might be entirely benign. There may exist conforming implementations of the CLI that will not accept metadata that violates an ERROR rule, and therefore such metadata is invalid and is not portable.

·              A WARNING reports something, not actually wrong, but possibly a slip on the part of the compiler.  Normally, it indicates a case where a compiler could have encoded the same information in a more compact fashion or where the metadata represents a construct that can have no actual use at runtime.  All conforming implementations will support metadata that violate only WARNING rules; hence such metadata is both valid and portable.

·              A CLS reports lack of compliance with common language specification (see Partition I_alink=Partition_I). Such metadata is both valid and portable, but there may exist programming languages that cannot process it, even though all conforming implementations of the CLI support the constructs.

Validation rules fall into a few broad categories, as follows:

·              Number of Rows  A few tables are allowed only one row (e.g. Module table).  Most have no such restriction.

·              Unique Rows No table may contain duplicate rows, where “duplicate” is defined in terms of its key column, or combination of columns

·              Valid Indexes Columns which are indexes shall point somewhere sensible, as follows:

o             Every index into the String, Blob or Userstring heaps shall point into that heap, neither before its start (offset 0), nor after its end

o             Every index into the Guid heap shall lie between 1 and the maximum element number in this module, inclusive

o             Every index (row number) into another metadata table shall lie between 0 and that table’s row count + 1  (for some tables, the index may point just past the end of any target table, meaning it indexes nothing)

·              Valid Bitmasks Columns which are bitmasks shall only have valid permutations of bits set

·              Valid RVAs  There are restrictions upon fields and methods that are assigned RVAs (Relative Virtual Addresses; these are byte offsets, expressed from the address at which the corresponding PE file is loaded into memory)

Note that some of the rules listed below say "nothing" - for example,  some rules state that a particular table is allowed zero or more rows - so there is no way that the check can fail.  This is done simply for completeness, to record that such details have indeed been addressed, rather than overlooked.

End informative text


The CLI imposes no limit on the size of names stored in metadata, and subsequently processed by a CLI implementation.

21.2      Assembly : 0x20

The Assembly table has the following columns:

·              HashAlgId (a 4 byte constant of type AssemblyHashAlgorithm, clause 22.1.1)

·              MajorVersion, MinorVersion, BuildNumber, RevisionNumber (2 byte constants)

·              Flags (a 4 byte bitmask of type AssemblyFlags, clause 22.1.2)

·              PublicKey (index into Blob heap)

·              Name (index into String heap)

·              Culture (index into String heap)

The Assembly table is defined using the .assembly directive (see Section 6.2); its columns are obtained from the respective .hash algorithm, .ver, .publickey, and .culture (see clause 6.2.1  For an example see  Section 6.2.


This contains informative text only

1.             The Assembly table may contain zero or one row  [ERROR]

2.             HashAlgId should be one of the specified values  [ERROR]

Implementation Specific (Microsoft)

The Microsoft implementation treats this as a WARNING rather than an error, using numbers based on the Crypto APIs.  This means that the Microsoft implementation can handle additional algorithms based on the constants of type ALG_CLASS_HASH in WinCrypt.h as well as those dynamically discovered at runtime.

3.             Flags may have only those values set that are specified   [ERROR]

4.             PublicKey may be null or non-null

5.             Name shall index a non-null string in the String heap  [ERROR]

6.             The string indexed by Name can be of unlimited length

7.             Culture may be null or non-null

8.             If Culture is non-null, it shall index a single string from the list specified (see clause 22.1.3) [ERROR]

Note: Name is a simple name (e.g., “Foo” - no drive letter, no path, no file extension); on POSIX-compliant systems Name contains no colon, no forward-slash, no backslash, no period.

End informative text


21.3      AssemblyOS : 0x22

The AssemblyOS table has the following columns:

·              OSPlatformID  (a 4 byte constant)

·              OSMajorVersion (a 4 byte constant)

·              OSMinorVersion (a 4 byte constant)

This record should not be emitted into any PE file.  If present in a PE file, it should be treated as if all its fields were zero.  It should be ignored by the CLI.

21.4      AssemblyProcessor : 0x21

The AssemblyProcessor table has the following column:

·              Processor (a 4 byte constant)

This record should not be emitted into any PE file.  If present in a PE file, it should be treated as if its field were zero.  It should be ignored by the CLI.

21.5      AssemblyRef : 0x23

The AssemblyRef table has the following columns:

·              MajorVersion, MinorVersion, BuildNumber, RevisionNumber (2 byte constants)

·              Flags (a 4 byte bitmask of type AssemblyFlags, clause 22.1.2)

·              PublicKeyOrToken (index into Blob heap – the public key or token that identifies the author of this Assembly)

·              Name (index into String heap)

·              Culture (index into String heap)

·              HashValue (index into Blob heap)

The table is defined by the .assembly extern directive (see Section 6.3). Its columns are filled using directives similar to those of the Assembly table except for the PublicKeyOrToken column which is defined using the .publickeytoken directive.  For an example see Section 6.3.

This contains informative text only

1.             MajorVersion, MinorVersion, BuildNumber, RevisionNumber can each have any value

2.             Flags may have only one possible bit set – the PublicKey bit (see clause 22.1.2).   All other bits shall be zero.    [ERROR]

3.             PublicKeyOrToken my be null, or non-null (note that the Flags.PublicKey bit specifies whether the 'blob' is a full public key, or the short hashed token)

4.             If non-null, then PublicKeyOrToken shall index a valid offset in the Blob heap  [ERROR]

5.             Name shall index a non-null string, in the String heap (there is no limit to its length). [ERROR]

6.             Culture may be null or non-null.  If non-null, it shall index a single string from the list specified   (see clause 22.1.3) [ERROR]

7.             HashValue may be null or non-null

8.             If non-null, then HashValue shall index a non-empty 'blob' in the Blob heap  [ERROR]

9.             The AssemblyRef table shall contain no duplicates, where duplicate rows have the same MajorVersion, MinorVersion, BuildNumber, RevisionNumber, PublicKeyOrToken, Name and Culture  [WARNING]

Note: Name is a simple name (e.g., “Foo” - no drive letter, no path, no file extension); on POSIX-compliant systems Name contains no colon, no forward-slash, no backslash, no period.End informative text

21.6      AssemblyRefOS : 0x25

The AssemblyRefOS table has the following columns:

·              OSPlatformId  (4 byte constant)

·              OSMajorVersion (4 byte constant)

·              OSMinorVersion (4 byte constant)

·              AssemblyRef  (index into the AssemblyRef table)

These records should not be emitted into any PE file.  If present in a PE file, they should be treated as-if their fields were zero.  They should be ignored by the CLI.

21.7      AssemblyRefProcessor : 0x24

The AssemblyRefProcessor table has the following columns:

·              Processor (4 byte constant)

·              AssemblyRef  (index into the AssemblyRef table)

These records should not be emitted into any PE file.  If present in a PE file, they should be treated as-if their fields were zero.  They should be ignored by the CLI.

21.8      ClassLayout : 0x0F

The ClassLayout table is used to define how the fields of a class or value type shall be laid out by the CLI (normally, the CLI is free to reorder and/or insert gaps between the fields defined for a class or value type). 

Rationale: This feature is used to make a managed value type be laid out in exactly the same way as an unmanaged C struct – with this condition true, the managed value type can be handed to unmanaged code, which accesses the fields exactly as if that block of memory had been laid out by unmanaged code.

The information held in the ClassLayout table depends upon the Flags value for {AutoLayout, SequentialLayout, ExplicitLayout} in the owner class or value type.

A type has layout if it is marked SequentialLayout or ExplicitLayout.  If any type within an inheritance chain has layout, then so shall all its parents, up to the one that descends immediately from System.Object, or from System.ValueType.


This contains informative text only

Layout cannot begin part way down the chain.  But it is legal to stop “having layout” at any point down the chain.

For example, in the diagrams below, Class A derives from System.Object; class B derives from A; class C derives from B.  System.Object has no layout.  But A, B and C are all defined with layout, and that is legal.

 

Similarly with Classes E, F and G.  G has no layout.  This too is legal.   The following picture shows two illegal setups:

 

On the left, the “chain with layout” does not start at the ‘highest’ class.  And on the right, there is a ‘hole’ in the “chain with layout”

Layout information for a class or value type is held in two tables – the ClassLayout and FieldLayout tables, as shown in this diagram:

This example shows how row 3 of the ClassLayout table points to row 2 in the TypeDef table (the definition for a Class, called “MyClass”).  Rows 4 through 6 of the FieldLayout table point to corresponding rows in the Field table.  This illustrates how the CLI stores the explicit offsets for the three fields that are defined in “MyClass” (there is always one row in the FieldLayout table for each field in the owning class or value type)   So, the ClassLayout table acts as an extension to those rows of the TypeDef table that have layout info;  since many classes do not have layout info, this design overall saves space

End informative text


The ClassLayout table has the following columns:

·              PackingSize (a 2 byte constant)

·              ClassSize (a 4 byte constant)

·              Parent (index into TypeDef table)

The rows of the ClassLayout table are defined by placing .pack and .size directives on the body of a parent type declaration (see Section 9.2).  For an example see Section 9.7.


This contains informative text only

1.             A ClassLayout table may contain zero or more or rows

2.             Parent shall index a valid row in the TypeDef table, corresponding to a Class or ValueType (not to an Interface)  [ERROR]

3.             The Class or ValueType indexed by Parent shall not be AutoLayout - i.e., it shall be one of SequentialLayout or ExplicitLayout. (See clause 22.1.14). Put another way, AutoLayout types shall not own any rows in the ClassLayout table.  [ERROR]

4.             If Parent indexes a SequentialLayout type, then:  [ERROR]

o             PackingSize shall be one of {0, 1, 2, 4, 8, 16, 32, 64, 128}  (0 means use the default pack size for the platform  that the application is running on)

o             if ClassSize is non-zero, then it shall be greater than or equal to the calculated size of the class, based upon its field sizes and PackingSize (compilers request padding at the end of a class by providing a value for ClassSize that is larger than its calculated size)  [ERROR]

o             a ClassSize of zero does not mean the class has zero size.  It means, no size was specified at definition time.  Instead, the actual size is calculated from the field types, taking account of packing size (default or specified) and natural alignment on the target, runtime platform

o             if Parent indexes a ValueType, then ClassSize shall be less than 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

Current implementation of desktop CLI allows 0x3F0000 bytes, but may be reduced in future

5.             Note that ExplicitLayout types might result in verifiable types, so long as that layout does not create union types.

6.             If Parent indexes an ExplicitLayout type, then  [ERROR]

o             if ClassSize is non-zero, then it shall be greater than or equal to the calculated size of the class, based upon the rows it owns in the FieldLayout table (compilers create padding at the end of a class by providing a value for ClassSize that is larger than its calculated size)

o             a ClassSize of zero does not mean the class has zero size.  It means, no size was specified at definition time.  Instead, the actual size is calculated from the field types, their specified offsets, and any beyond-end alignment packing performed by the target platform

o             if Parent indexes a ValueType, then ClassSize shall be less than 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

Current implementation allows 0x3F0000 bytes, but may be reduced in future

o             PackingSize shall be 0 (because it makes no sense to provide explicit offsets for each field, as well as a packing size)

7.             Layout along the length of an inheritance chain shall follow the rules specified above (starts at ‘highest’ Type, with no ‘holes’, etc)   [ERROR]

End informative text


21.9      Constant : 0x0B

The Constant table is used to store compile-time, constant values for fields, parameters and properties.

The Constant table has the following columns:

·              Type (a 1 byte constant, followed by a 1-byte padding zero) : see Clause 22.1.15

·              Parent (index into the Param or Field or Property table; more precisely, a HasConst coded index)

·              Value (index into Blob heap)

Note that Constant information does not directly influence runtime behavior.  Compilers inspect this information, at compile time, when importing metadata; but the value of the constant itself, if used, becomes embedded into the CIL stream the compiler emits.  There are no CIL instructions to access the Constant table at runtime.

A row in the Constant table for a parent is created whenever a compile-time value is specified for that parent, for an example see Section 15.2.

This contains informative text only

1.             Type shall be exactly one of: ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, ELEMENT_TYPE_STRING; or ELEMENT_TYPE_CLASS with a Value of zero  (See clause 22.1.15) [ERROR]

Note: the last option, ELEMENT_TYPE_CLASS with a Value of zero, is used to denote a null object reference, and corresponds to the nullref value for <fieldInit> in ilasm (see Section 15.2) .  Unlike uses of ELEMENT_TYPE_CLASS in signatures, this one is not followed by a type token, because null can apply legally to any and all object types.

2.             Type shall not be any of: ELEMENT_TYPE_I1, ELEMENT_TYPE_U2, ELEMENT_TYPE_U4, ELEMENT_TYPE_U8 (See clause 22.1.15)  [CLS]

3.             Parent shall index a valid row in the Field or Property or Param table  [ERROR]

4.             There shall be no duplicate rows, based upon Parent  [ERROR]

5.            Constant.Type must match exactly the declared type of the Param, Field or Property identified by Parent  (in the case where the parent is an enum, it must match exactly the underlying type of that enum) [CLS]

End informative text

21.10   CustomAttribute : 0x0C

The CustomAttribute table has the following columns:

·              Parent (index into any metadata table, except the CustomAttribute table itself; more precisely, a HasCustomAttribute coded index)

·              Type (index into the Method or MethodRef table; more precisely, a CustomAttributeType coded index)

·              Value (index into Blob heap)

The CustomAttribute table stores data that can be used to instantiate a Custom Attribute (more precisely, an object of the specified Custom Attribute class) at runtime.  The column called Type is slightly misleading – it actually indexes a constructor method – the owner of that constructor method is the Type of the Custom Attribute.

A row in the CustomAttribute table for a parent is created by the .custom  attribute, which gives the value of the Type column and optionally that of the Value column (see Chapter 20)

This contains informative text only

All binary values are stored in little-endian format (except PackedLen items - used only as counts for the number of bytes to follow in a UTF8 string)

1.             It is legal for there to be no CustomAttribute present at all - that is, for the CustomAttribute.Value field  to be null

2.             Parent can be an index into any metadata table, except the CustomAttribute table itself  [ERROR]

3.             Type shall index a valid row in the Method or MethodRef table.  That row shall be a constructor method (for the class of which this information forms an instance)  [ERROR]

4.             Value may be null or non-null

5.             If Value is non-null, it shall index a 'blob' in the Blob heap [ERROR]

6.             The following rules apply to the overall structure of the Value 'blob'(see Section 22.3):

o             Prolog shall be 0x0001  [ERROR]

o             There shall be as many occurrences of FixedArg as are declared in the Constructor method  [ERROR]

o             NumNamed may be zero or more

o             There shall be exactly NumNamed occurrences of NamedArg [ERROR]

o             Each NamedArg shall be accessible by the caller  [ERROR]

o             If NumNamed = 0 then there shall be no further items in the CustomAttrib [ERROR]

7.             The following rules apply to the structure of FixedArg (see Section 22.3):

o             If this item is not for a vector (a single-dimension array with lower bound of 0), then there shall be exactly one Elem  [ERROR]

o             If this item is for a vector, then:

o             NumElem shall be 1 or more  [ERROR]

o             This shall be followed by NumElem occurrences of Elem [ERROR]

8.             The following rules apply to the structure of Elem (see Section 22.3):

o             If this is a simple type or an enum (see Section 22.3 for how this is defined), then Elem consists simply of its value  [ERROR]

o             If this is a string, or a Type, then Elem consists of a SerString – PackedLen count of bytes, followed by the UTF8 characters   [ERROR]

o             If this is a boxed simple value type (bool, char, float32, float64, int8, int16, int32, int64, unsigned int8, unsigned int16, unsigned int32 or unsigned int64), then Elem consists of the corresponding type denoter (ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8), followed by its value.  [ERROR]

9.             The following rules apply to the structure of  NamedArg (see Section 22.3):

o             The single byte FIELD (0x53) or PROPERTY (0x54)  [ERROR]

o             The type of the field or property -- one of ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, ELEMENT_TYPE_STRING or the constant 0x50 (for an argument of type System.Type)

o             The name of the Field or Property, respectively with the previous item, as a SerString – PackedLen count of bytes, followed by the UTF8 characters of the name  [ERROR]

o             A FixedArg  (see above)  [ERROR]

End informative text

21.11   DeclSecurity : 0x0E

Security attributes, which derive from System.Security.Permissions.SecurityAttribute (see Partition IV_alink=Partition_IV), can be attached to a TypeDef, a Method or to an Assembly. All constructors of this class shall take a System.Security.Permissions.SecurityAction value as their first parameter, describing what should be done with the permission on the type, method or assembly to which it is attached.  Code access security attributes, which derive from System.Security.Permissions.CodeAccessSecurityAttribute, may have any of the security actions.

These different security actions are encoded in the DeclSecurity table as a 2-byte enum (see below).  All security custom attributes for a given security action on a method, type or assembly shall be gathered together and one System.Security.PermissionSet instance shall be created, stored in the Blob heap, and referenced from the DeclSecurity table.

Note: The general flow from a compiler’s point of view is as follows.  The user specifies a custom attribute through some language-specific syntax that encodes a call to the attribute’s constructor. If the attribute’s type is derived (directly or indirectly) from System.Security.Permissions.SecurityAttribute then it is a security custom attribute and requires special treatment, as follows (other custom attributes are handled by simply recording the constructor in the metadata as described in Section 21.10). The attribute object is constructed, and provides a method (CreatePermission) to convert it into a security permission object (an object derived from System.Security.Permission). All the permission objects attached to a given metadata item with the same security action are combined together into a System.Security.PermissionSet.  This permission set is converted into a form that is ready to be stored in XML using its ToXML method to create a System.Security.SecurityElement. Finally, the XML that is required for the metadata is created using the ToString method on the security element.

The DeclSecurity table  has the following columns:

·              Action (2 byte value)

·              Parent (index into the TypeDef, Method or Assembly table; more precisely, a HasDeclSecurity coded index)

·              PermissionSet (index into Blob heap)

Action is a 2-byte representation of Security Actions, see System.Security.SecurityAction in Partition IV_alink=Partition_IV. The values 0 through 0xFF are reserved for future standards use.  Values 0x20 through 0x7F and 0x100 through 0x07FF are for uses where the action may be ignored if it is not understood or supported.  Values 0x80 through 0xFF and 0x0800 through 0xFFFF are for uses where the action shall be implemented for secure operation; in implementations where the action is not available no access to the assembly, type, or method shall be permitted.

Security Action

Note

Explanation of behavior

Legal Scope

Assert

1

Without further checks satisfy Demand for specified permission

Method, Type

Demand

1

Check all callers in the call chain have been granted specified permission, throw SecurityException (see Partition IV_alink=Partition_IV) on failure

Method, Type

Deny

1

Without further checks refuse Demand for specified permission

Method, Type

InheritanceDemand

1

Specified permission shall be granted in order to inherit from class or override virtual method.

Method, Type

LinkDemand

1

Check immediate caller has been granted specified permission, throw SecurityException (see Partition IV_alink=Partition_IV) on failure

Method, Type

PermitOnly

1

Without further checks refuse Demand for all permissions other than those specified.

Method, Type

RequestMinimum

 

Specify minimum permissions required to run

Assembly

RequestOptional

 

Specify optional permissions to grant

Assembly

RequestRefuse

 

Specify permissions not to be granted

Assembly

NonCasDemand

2

Check that current assembly has been granted specified permission, throw SecurityException (see Partition IV_alink=Partition_IV) otherwise

Method, Type

NonCasLinkDemand

2

Check that immediate caller has been granted specified permission, throw SecurityException (see Partition IV_alink=Partition_IV) otherwise

Method, Type

PrejitGrant

 

Reserved for implementation-specific use

Assembly

 

Note 1: Specified attribute shall derive from System.Security.Permissions.CodeAccess-SecurityAttribute

Note 2: Attribute shall derive from System.Security.Permissions.SecurityAttribute, but shall not derive from System.Security.Permissions.CodeAccessSecurityAttribute

Parent is a Meta Data token that identifies the Method, Type or Assembly on which security custom attributes serialized in PermissionSet was defined.

PermissionSet is a 'blob' that contains the XML serialization of a permission set.  The permission set contains the permissions that were requested with an Action on a specific Method, Type or Assembly (see Parent).

The rows of the DeclSecurity table are filled by attaching a .permission or .permissionset directive that specifies the Action and PermissionSet on a parent assembly (see Section 6.6) or parent type or method (see Section 9.2).

This contains informative text only

1.             Action  may have only those values set that are specified [ERROR]

2.             Parent shall be one of TypeDefMethodDef, or  Assembly.   That is, it shall index a valid row in the TypeDef table, the MethodDef table, or the Assembly table  [ERROR]

3.             If Parent indexes a row in the TypeDef table, that row should not define an Interface.  The security system ignores any such parent; compilers should not emit such permissions sets  [WARNING]

4.             If Parent indexes a TypeDef, then its TypeDef.Flags.HasSecurity bit should be set  [ERROR]

5.             If Parent indexes a MethodDef, then its MethodDef.Flags.HasSecurity bit should be set  [ERROR]

6.             PermissionSet should index a 'blob' in the Blob heap  [ERROR]

7.             The format of the 'blob' indexed by PermissionSet should represent a valid, serialized CLI object graph.  The serialized form of all standardized permissions is specified in Partition IV_alink=Partition_IV. [ERROR]

End informative text

21.12   EventMap : 0x12

The EventMap table has the following columns:

·              Parent (index into the TypeDef table)

·              EventList (index into Event table).  It marks the first of a contiguous run of Events owned by this Type.  The run continues to the smaller of:

o             the last row of the Event table

o             the next run of Events, found by inspecting the EventList of the next row in the EventMap  table

Note that EventMap info does not directly influence runtime behavior; what counts is the info stored for each method that the event comprises.

This contains informative text only

1.             EventMap table may contain zero or more rows

2.             There shall be no duplicate rows, based upon Parent (a given class has only one ‘pointer’ to the start of its event list)  [ERROR]

3.             There shall be no duplicate rows, based upon EventList (different classes cannot share rows in the Event table)  [ERROR]

End informative text

21.13   Event : 0x14

Events are treated within metadata much like Properties – a way to associate a collection of methods defined on given class.  There are two required methods – add_ and remove_, plus optional raise_ and others.  All of the methods gathered together as an Event shall be defined on the class.

The association between a row in the TypeDef table and the collection of methods that make up a given Event, is held in three separate tables (exactly analogous to that used for Properties) – see the below:

 

Row 3 of the EventMap table indexes row 2 of the TypeDef table on the left (MyClass), whilst indexing row 4 of the Event table on the right – the row for an Event called DocChanged.  This setup establishes that MyClass has an Event called DocChanged.  But what methods in the Method table are gathered together as ‘belonging’ to event DocChanged?  That association is contained in the MethodSemantics table – its row 2 indexes event DocChanged to the right, and row 2 in the Method table to the left (a method called add_DocChanged).  Also, row 3 of the MethodSemantics table indexes DocChanged to the right, and row 3 in the Method table to the left (a method called remove_DocChanged). As the shading suggests, MyClass has another event, called TimedOut, with two methods, add_TimedOut and remove_TimedOut.

Event tables do a little more than group together existing rows from other tables.  The Event table has columns for EventFlags, Name (eg DocChanged and TimedOut in  the example here) and EventType.  In addition, the MethodSemantics table has a column to record whether the method it points at is an add_, a remove_, a raise_, or other.

The Event table has the following columns:

·              EventFlags (a 2 byte bitmask of type EventAttribute, clause 22.1.4)

·              Name (index into String heap)

·              EventType (index into TypeDef, TypeRef or TypeSpec tables; more precisely, a TypeDefOrRef coded index)  [this corresponds to the Type of the Event; it is not the Type that owns this event]

Note that Event information does not directly influence runtime behavior; what counts is the information stored for each method that the event comprises.

The EventMap and Event tables result from putting the .event directive on a class (see Chapter 17).

This contains informative text only

1.             The Event table may contain zero or more rows

2.             Each row shall have one, and only one, owner row in the EventMap table [ERROR]

3.             EventFlags may have only those values set that are specified (all combinations valid)  [ERROR]

4.             Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

5.             The Name string shall be a valid CLS identifier  [CLS]

6.             EventType may be null or non-null

7.             If EventType is non-null, then it shall index a valid row in the TypeDef or TypeRef table  [ERROR]

8.             If EventType is non-null, then the row in TypeDef , TypeRef, or TypeSpec table that it indexes shall be a Class (not an Interface; not a ValueType)  [ERROR]

9.             For each row, there shall be one add_ and one remove_ row in the MethodSemantics table  [ERROR]

10.          For each row, there can be zero or one raise_ row, as well as zero or more other rows in the MethodSemantics table  [ERROR]

11.          Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name  [ERROR]

12.          There shall be no duplicate rows based upon Name, where Name fields are compared using CLS conflicting-identifier-rules  [CLS]

End informative text

21.14   ExportedType : 0x27

The ExportedType table holds a row for each type, defined within other modules of this Assembly, that is exported out of this Assembly.  In essence, it stores TypeDef row numbers of all types that are marked public in other modules that  this Assembly comprises. 

The actual target row in a TypeDef table is given by the combination of TypeDefId (in effect, row number) and Implementation (in effect, the module that holds the target TypeDef table).  Note that this is the only occurrence in metadata of foreign tokens – that is token values  that have a meaning in another module.  (Regular token values are indexes into table in the current module)

The full name of the type need not be stored directly.  Instead, it may be split into two parts at any included “.” (although typically this done at the last “.” in the full name).  The part preceding the “.” is stored as the TypeNamespace and that following the “.” is stored as the TypeName. If there is no “.” in the full name, then the TypeNamespace shall be the index of the empty string.

The ExportedType table has the following columns:

·              Flags (a 4 byte bitmask of type TypeAttributes, clause 22.1.14)

·              TypeDefId (4 byte index into a TypeDef table of another module in this Assembly).  This field is used as a hint only.  If the entry in the target TypeDef table matches the TypeName and TypeNamespace entries in this table, resolution has succeeded.  But if there is a mismatch, the CLI shall fall back to a search of the target TypeDef table

·              TypeName (index into the String heap)

·              TypeNamespace (index into the String heap)

·              Implementation.  This can be an index (more precisely, an Implementation coded index) into one of 2 tables, as follows:

o             File table, where that entry says which module in the current assembly holds the TypeDef

o             ExportedType table, where that entry is the enclosing Type of the current nested Type

The rows in the ExportedType table are the result of the .class extern directive (see Section 6.7).

This contains informative text only

The term “FullName” refers to the string created as follows: if the TypeNamespace is null, then use the TypeName, otherwise use the concatenation of Typenamespace, “.”, and TypeName.

1.             The ExportedType table may contain zero or more rows

2.             There shall be no entries in the ExportedType table for Types that are defined in the current module - just for Types defined in other modules within the Assembly  [ERROR]

3.             Flags may have only those values set that are specified   [ERROR]

4.             If Implementation indexes the File table, then Flags.VisibilityMask shall be public (see clause 22.1.14) [ERROR]

5.             If Implementation indexes the ExportedType table, then Flags.VisibilityMask shall be NestedPublic (see  see clause 22.1.14)  [ERROR]

6.             If non-null, TypeDefId should index a valid row in a TypeDef table in a module somewhere within this Assembly (but not this module), and the row so indexed should have its Flags.Public = 1  (see  see clause 22.1.14)  [WARNING]

7.             TypeName shall index a non-null string in the String heap [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME 

8.             TypeNamespace may be null, or non-null

9.             If TypeNamespace is non-null, then it shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME.  Also, the FullName (concatenated TypeNamespace + "."  + TypeName) shall be less than MAX_CLASS_NAME

10.          FullName shall be a valid CLS identifier  [CLS]

11.          If this is a nested Type, then TypeNamespace should be null, and TypeName should represent the unmangled, simple name of the nested Type  [ERROR]

12.          Implementation shall be a valid index into either:  [ERROR]

·              the File table; that file shall hold a definition of the target Type in its TypeDef table

·              a different row in the current ExportedType table - this identifies the enclosing Type of the current, nested Type

13.          FullName shall match exactly the corresponding FullName for the row in the TypeDef table indexed by TypeDefId  [ERROR]

14.          Ignoring nested Types, there shall be no duplicate rows, based upon FullName [ERROR]

15.          For nested Types, there shall be no duplicate rows, based upon TypeName and enclosing Type  [ERROR]

16.          The complete list of Types exported from the current Assembly is given as the catenation of the ExportedType table with all public Types in the current TypeDef table, where “public” means a Flags.tdVisibilityMask of either Public or NestedPublic.  There shall be no duplicate rows, in this concatenated table, based upon FullName (add Enclosing Type into the duplicates check if this is a nested Type)  [ERROR]

End informative text

21.15   Field : 0x04

The Field table has the following columns:

·              Flags (a 2 byte bitmask of type FieldAttributes, clause 22.1.5)

·              Name (index into String heap)

·              Signature (index into Blob heap)

Conceptually, each row in the Field table is owned by one, and only one, row in the TypeDef table. However, the owner of any row in the Field table is not stored anywhere in the Field table itself.   There is merely a ‘forward-pointer’ from each row in the TypeDef table (the FieldList column), as shown in the following illustration. 

The TypeDef table has rows 1 through 4.  The first row in the TypeDef table corresponds to a pseudo type, inserted automatically by the CLI.  It is used to denote those rows in the Field table corresponding to global variables.  The Field table has rows 1 through 6.  Type 1 (pseudo type for ‘module’) owns rows 1 and 2 in the Field table.  Type 2 owns no rows in the Field table, even  though its FieldList indexes row 3 in the Field table.  Type 3 owns rows 3 through 5 in the Field table.  Type 4 owns row 6 in the Field table.  (The next pointers in the diagram show the next free row in each table)  So, in the Field table, rows 1 and 2 belong to Type 1 (global variables); rows 3 through 5 belong to Type 3; row 6 belongs to Type 4.

Each row in the Field table results from a toplevel .field directive (see Section 5.10), or a .field directive inside a Type (see Section 9.2).  For an example see Section 13.5.

This contains informative text only

1.             Field table may contain zero or more rows

2.             Each row shall have one, and only one, owner row in the TypeDef table [ERROR]

3.             The owner row in the TypeDef table shall not be an Interface [CLS]

4.             Flags may have only those set that are specified  [ERROR]

5.             The FieldAccessMask subfield of Flags shall contain precisely one of Compilercontrolled, Private, FamANDAssem, Assembly, Family, FamORAssem, or Public  (see clause 22.1.5)  [ERROR]

6.             Flags may set 0 or 1 of Literal or InitOnly (not both) (see clause 22.1.5)   [ERROR]

7.             If Flags.Literal = 1 then Flags.Static shall be 1 too  (see clause 22.1.5)  [ERROR]

8.             If Flags.RTSpecialName = 1, then Flags.SpecialName shall also be 1  (see clause 22.1.5)  [ERROR]

9.             If Flags.HasFieldMarshal = 1, then this row shall ‘own’ exactly one row in the FieldMarshal table  (see clause 22.1.5)   [ERROR]

10.          If Flags.HasDefault = 1, then this row shall ‘own’ exactly one row in the Constant table  (see clause 22.1.5)  [ERROR]

11.          If Flags.HasFieldRVA = 1, then this row shall ‘own’ exactly one row in the Field’s RVA table  (see clause 22.1.5)   [ERROR]

12.          Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

13.          The Name string shall be a valid CLS identifier  [CLS]

14.          Signature shall index a valid field signature in the Blob heap [ERROR]

15.          If Flags.Compilercontrolled = 1 (see clause 22.1.5), then this row is ignored completely in duplicate checking.

16.          If the owner of this field is the internally-generated type called <Module>, it denotes that this field is defined at module scope (commonly called a global variable). In this case:

o             Flags.Static shall be 1  [ERROR]

o             Flags.MemberAccessMask subfield shall be one of Public, Compilercontrolled, or Private  (see clause 22.1.5)  [ERROR]

o             module-scope fields are not allowed  [CLS]

17.          There shall be no duplicate rows in the Field table, based upon owner+Name+Signature (where owner is the owning row in the TypeDef table, as described above)  (Note however that if Flags.Compilercontrolled = 1, then this row is completely excluded from duplicate checking)  [ERROR]

18.          There shall be no duplicate rows in the Field table, based upon owner+Name, where Name fields are compared using CLS conflicting-identifier-rules.  So, for example,"int i" and "float i" would be considered CLS duplicates.  (Note however that if Flags.Compilercontrolled = 1, then this row is completely excluded from duplicate checking, as noted above)  [CLS]

19.          If this is a field of an Enum, and Name string = "value__" then:

a.            RTSpecialName shall be 1  [ERROR]

b.            owner row in TypeDef table shall derive directly from System.Enum [ERROR]

c.            the owner row in TypeDef table shall have no other instance fields  [CLS]

d.            its Signature shall be one of  (see clause 22.1.15 ): [CLS]

·              ELEMENT_TYPE_U1

·              ELEMENT_TYPE_I2

·              ELEMENT_TYPE_I4

·              ELEMENT_TYPE_I8

20.          its Signature shall be an integral type.

End informative text

21.16   FieldLayout : 0x10

The FieldLayout table has the following columns:

·              Offset (a 4 byte constant)

·              Field (index into the Field table)

Note that each Field in any Type is defined by its Signature. When a Type instance (ie, an object) is laid out by the CLI, each Field is one of three kinds:

·              Scalar – for any member of built-in, such as int32.  The size of the field is given by the size of that intrinsic, which varies between 1 and 8 bytes

·              ObjectRef – for CLASS, STRING, OBJECT, ARRAY, SZARRAY

·              Pointer – for PTR, FNPTR

·              ValueType – for VALUETYPE.  The instance of that ValueType is actually laid out in this object, so the size of the field is the size of that ValueType

(This lists above use an abbreviation – each all-caps name should be prefixed by ELEMENT_TYPE_ so, for example, STRING is actually ELEMENT_TYPE_STRING. See clause 22.1.15)

Note that metadata specifying explicit structure layout may be valid for use on one platform but not another, since some of the rules specified here are dependent on platform-specific alignment rules.

A row in the FieldLayout table is created if the .field directive for the parent field has specified a field offset (see Section 9.7).

This contains informative text only

1.             A FieldLayout table may contain zero or more or rows

2.             The Type whose Fields are described by each row of the FieldLayout table shall have Flags.ExplicitLayout (see clause 22.1.14) set  [ERROR]

3.             Offset shall be zero or more (cannot be negative)  [ERROR]

4.             Field shall index a valid row in the Field table  [ERROR]

5.             The row in the Field table indexed by Field shall be non-static (ie its Flags.Static shall be 0)  [ERROR]

6.             Among the rows owned by a given Type there shall be no duplicates, based upon Field.  That is,  a given Field of a Type cannot be given two offsets.   [ERROR]

7.             Each Field of kind ObjectRef shall be naturally aligned within the Type  [ERROR]

8.             No Field of kind ObjectRef may overlap any other Field no matter what its kind, wholly or partially  [ERROR]

9.             Among the rows owned by a given Type it is perfectly legal for several rows to have the same value of Offset, so long as they are not of type ObjectRef (used to define C unions, for example) [ERROR]

10.          If ClassSize in the owner ClassLayout row is non-zero, then no Field may extend beyond that ClassSize (ie, the Field Offset value plus the Field’s calculated size shall not exceed ClassSize) (note that  it is legal, and common, for ClassSize to be supplied as larger than the calculated object size - the CLI pads the object with trailing bytes up to the ClassSize value)  [ERROR]

11.          Every Field of an ExplicitLayout Type shall be given an offset - that is, it shall have a row in the FieldLayout table  [ERROR]

Implementation Specific (Microsoft)

Note that the rules above specify what is legal, or non-legal, metadata. However, there is a finer distinction that can be drawn - what layouts permit type-safe access by code?  For example, a class that overlaps two ValueTypes constitutes legal metadata, but accesses to that class may result in code that is not provably typesafe.  At runtime, it is the Class loader that will perform these type-safety checks.   Version 1 takes a simple approach - if the type has any explicit layout, it is  not typesafe. [This may be refined in future versions.]

End informative text

21.17   FieldMarshal : 0x0D

The FieldMarshal table has two columns.  It ‘links’ an existing row in the Field or Param table, to information in the Blob heap that defines how that field or parameter (which, as usual, covers the method return, as parameter number 0) should be marshalled when calling to or from unmanaged code via PInvoke dispatch.

Note that FieldMarshal information is used only by code paths that arbitrate operation with unmanaged code.  In order to execute such paths, the caller, on most platforms, would be installed with elevated security permission.  Once it invokes unmanaged code, it lies outside the regime that the CLI can check - it is simply trusted not to violate the type system.

The FieldMarshal table has the following columns:

·              Parent (index into Field or Param table; more precisely, a HasFieldMarshal coded index)

·              NativeType (index into the Blob heap)

For the detailed format of the 'blob', see Section 22.4

A row in the FieldMarshal table is created if the .field directive for the parent field has specified a .marshall attribute (see Section 15.1).

This contains informative text only

1.             A FieldMarshal table may contain zero or more rows

2.             Parent shall index a valid row in the Field or Param table (Parent values are encoded to say which of these two tables each refers to) [ERROR]

3.             NativeType shall index a non-null 'blob' in the Blob heap [ERROR]

4.             No two rows can point to the same parent.  In other words, after the Parent values have been decoded to determine whether they refer to the Field or the Param table, no two rows can point to the same row in the Field table or in the Param table [ERROR]

5.             The following checks apply to the MarshalSpec 'blob' (see Section 22.4):

a.            NativeIntrinsic shall be exactly one of the constant values in its production  [ERROR]

b.            If NativeIntrinsic has the value BYVALSTR, then Parent shall point to a row in the Field table, not the Param table  [ERROR]

c.            If FIXEDARRAY, then Parent shall point to a row in the Field table, not the Param table  [ERROR]

d.            If FIXEDARRAY, then NumElem shall be 1 or more  [ERROR]

e.            If FIXEDARRAY, then ArrayElemType shall be exactly one of the constant values in its production  [ERROR]

f.             If ARRAY, then ArrayElemType shall be exactly one of the constant values in its production  [ERROR]

g.            If ARRAY, then ParamNum may be zero

h.            If ARRAY, then ParamNum cannot be < 0  [ERROR]

i.              If ARRAY, and ParamNum > 0, then Parent shall point to a row in the Param table, not in the Field table  [ERROR]

j.              If ARRAY, and ParamNum > 0, then ParamNum cannot exceed the number of parameters supplied to the MethodDef (or MethodRef if a VARARG call) of which the parent Param is a member  [ERROR]

k.            If ARRAY, then ElemMult shall be >= 1  [ERROR]

l.              If ARRAY and ElemMult <> 1 issue a warning, because  it is probably a mistake  [WARNING]

m.           If ARRAY and ParamNum == 0, then NumElem shall be >= 1  [ERROR]

n.            If ARRAY and ParamNum != 0 and NumElem != 0 then issue a warning, because  it is probably a mistake  [WARNING]

Implementation Specific (Microsoft)

The following rules apply to Microsoft-specific features:

a. If CUSTOMMARSHALLER, then Guid shall be an in-place, counted-UTF8 string, that represents a string format GUID.  Its length, when expanded from UTF8, shall be exactly 38 characters, to include lead { and trailing }  [ERROR]

b. If CUSTOMMARSHALLER, then UnmanagedType shall be a non-empty, counted-UTF8 string  [ERROR]

c. If CUSTOMMARSHALLER, then ManagedType shall be a non-empty, counted-UTF8 string, that represents the fully-qualified namespace+"."+name of a Class or ValueType defined somewhere within the current Assembly  [ERROR]

d. If CUSTOMMARSHALLER, then the Cookie shall be a counted-UTF8 string - its size may legally be zero [ERROR]

e. If SAFEARRAY, then SafeArrayElemType shall be exactly one of the constant values in its production  [ERROR]

End informative text

21.18   FieldRVA : 0x1D

The FieldRVA table has the following columns:

·              RVA (a 4 byte constant)

·              Field (index into Field table)

Conceptually, each row in the FieldRVA table is an extension to exactly one row in the Field table, and records the RVA (Relative Virtual Address) within the image file at which this field’s initial value is stored.

A row in the FieldRVA table is created for each static parent field that has specified the optional data label (see Chapter 15).  The RVA column is the relative virtual address of the data in the PE file (see Section 15.3).

This contains informative text only

1.             RVA shall be non-zero  [ERROR]

2.             RVA shall point into the current module’s data area (not its metadata area)  [ERROR]

3.             Field shall index a valid table in the Field table [ERROR]

4.             Any field with an RVA shall be a ValueType (not a Class, and not an Interface).  Moreover, it shall not have any private fields (and likewise for any of its fields that are themselves ValueTypes).  (If any of these conditions were breached, code could overlay that global static and access its private fields.)  Moreover, no fields of that ValueType can be Object References (into the GC heap)  [ERROR]

5.             So long as two RVA-based fields comply with the previous conditions, the ranges of memory spanned by the two ValueTypes may overlap, with no further constraints.  This is not actually an additional rule; it simply clarifies the position with regard to overlapped RVA-based fields

End informative text

21.19   File : 0x26

The File table has the following columns:

·              Flags (a 4 byte bitmask of type FileAttributes, clause 22.1.6)

·              Name (index into String heap)

·              HashValue (index into Blob heap)

The rows of the File table result from .file directives in an Assembly (see clause 6.2.3)

This contains informative text only

1.             Flags may have only those values set that are specified (all combinations valid)  [ERROR]

2.             Name shall index a non-null string in the String heap.  It shall be in the format <filename>.<extension>  (eg “foo.dll”, but not “c:\utils\foo.dll”)  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_PATH_NAME

Also, the following values for Name are illegal (these represent device, rather than file, names):

S [N] [[C]*] where:

S ::= con | aux | lpt | prn | null | com   (case-blind)
N ::= a number 0 .. 9
C ::= $ | :

[] denotes optional, * denotes Kleene closure, | denotes alternatives  [ERROR]

The CLI also checks dynamically against opening a device, which can be assigned an arbitrary name by the user

3.             HashValue shall index a non-empty 'blob' in the Blob heap [ERROR]

4.             There shall be no duplicate rows - rows with the same Name value [ERROR]

5.             If this module contains a row in the Assembly table (that is, if this module “holds the manifest”) then there shall not be any row in the File table for this module - i.e., no self-reference  [ERROR]

6.             If the File table is empty, then this, by definition, is a single-file assembly.  In this case, the ExportedType table should be empty  [WARNING]

End informative text

21.20   ImplMap : 0x1C

The ImplMap table holds information about unmanaged methods that can be reached from managed code, using PInvoke dispatch.

Each row of the ImplMap table associates a row in the Method table (MemberForwarded) with the name of a routine (ImportName) in some unmanaged DLL (ImportScope). 

Note: A typical example would be: associate the managed Method stored in row N of the Method table (so MemberForwarded would have the value N) with the routine called “GetEnvironmentVariable” (the string indexed by ImportName) in the DLL called “kernel32” (the string in the ModuleRef table indexed by ImportScope). The CLI intercepts calls to managed Method number N, and instead forwards them as calls to the unmanged routine called “GetEnvironmentVariable” in “kernel32.dll” (including marshalling any arguments, as required)

The CLI does not support this mechanism to access fields that are exported from a DLL  --  only methods.

The ImplMap table has the following columns:

·              MappingFlags (a 2 byte bitmask of type PInvokeAttributes, clause 22.1.7)

·              MemberForwarded (index into the Field or Method table; more precisely, a MemberForwarded coded index.  However, it only ever indexes the Method table, since Field export is not supported.

·              ImportName (index into the String heap)

·              ImportScope (index into the ModuleRef table)

A row is entered in the ImplMap table for each parent Method (see Section 14.5) that is defined with a .pinvokeimpl interoperation attribute specifying the MappingFlags, ImportName and ImportScope. For an example see Section 14.5.

This contains informative text only

1.             ImplMap may contain zero or more rows

2.             MappingFlags may have only those values set that are specified [ERROR]

3.             MemberForwarded shall index a valid row in the Method table [ERROR]

4.             The MappingFlags.CharSetMask (see clause 22.1.7) in the row of the Method table indexed by MemberForwarded shall have at most one of the following bits set: CharSetAnsi, CharSetUnicode, or CharSetAuto} (if none set, the default is CharSetNotSpec)  [ERROR]

Implementation Specific (Microsoft)

The MappingFlags.CallConvMask in the row of the Method table indexed by MemberForwarded may have at most one of the following values: CallConvWinapi, CallConvCdecl, CallConvStdcall. It cannot have the value CallConvFastcall or CallConvThiscall  [ERROR]

5.             ImportName shall index a non-null string in the String heap [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

6.             ImportScope shall index a valid row in the ModuleRef table  [ERROR]

7.             The row indexed in the Method table by MemberForwarded shall have its Flags.PinvokeImpl = 1, and Flags.Static = 1  [ERROR]

End informative text

21.21   InterfaceImpl : 0x09

The InterfaceImpl table has the following columns:

·              Class (index into the TypeDef table)

·              Interface (index into the TypeDef, TypeRef or TypeSpec table; more precisely, a TypeDefOrRef coded index)

The InterfaceImpl table records which interfaces a Type implements.  Conceptually, each row in the InterfaceImpl table says that Class implements Interface.

This contains informative text only

1.             The InterfaceImpl table may contain zero or more rows

2.             Class shall be non-null [ERROR]

Implementation Specific (Microsoft)

If Class = null this row should be treated as if it does not exist.  Used to mark a class deleted, in incremental compilation scenarios, without physically deleting its metadata.

3.             If Class is non-null, then:

a.            Class shall index a valid row in the TypeDef table [ERROR]

b.            Interface shall index a valid row in the TypeDef or TypeRef table  [ERROR]

c.            The row in the TypeDef, TypeRef or TypeSpec  table indexed by Interface shall be an interface (Flags.Interface = 1), not a Class or ValueType  [ERROR]

4.             There should be no duplicates in the InterfaceImpl table, based upon non-null- Class and Interface values  [WARNING]

5.             There can be many rows with the same value for Class (a class can implement many interfaces)

6.             There can be many rows with the same value for Interface (many classes can implement the same interface)

End informative text

21.22   ManifestResource : 0x28

The ManifestResource table has the following columns:

·              Offset  (a 4 byte constant)

·              Flags (a 4 byte bitmask of type ManifestResourceAttributes, clause 22.1.8)

·              Name (index into the String heap)

·              Implementation (index into File table, or AssemblyRef table, or  null; more precisely, an Implementation coded index)

The Offset specifies the byte offset within the referenced file at which this resource record begins.  The Implementation specifies which file holds this resource.  The rows in the table result from .mresource directives on the Assembly (see clause 6.2.2).

This contains informative text only

1.             The ManifestResource table may contain zero or more rows

2.             Offset shall be a valid offset into the target file, starting from the Resource entry in the COR header  [ERROR]

3.             Flags may have only those values set that are specified  [ERROR]

4.             The VisibilityMask (see clause 22.1.8) subfield of Flags shall be one of Public or Private  [ERROR]

5.             Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

6.             Implementation may be null or non-null (if null, it means the resource is stored in the current file)

7.             If Implementation is null, then Offset shall be a valid offset in the current file, starting from the Resource entry in the CLI header [ERROR]

8.             If Implementation is non-null, then it shall index a valid row in the File or AssemblyRef table  [ERROR]

9.             There shall be no duplicate rows, based upon Name  [ERROR]

10.          If the resource is an index into the File table, Offset shall be zero  [ERROR]

End informative text

21.23   MemberRef : 0x0A

The MemberRef table combines two sorts of references – to Fields and to Methods of a class, known as ‘MethodRef’ and ‘FieldRef’, respectively.    The MemberRef table has the following columns:

·              Class (index into the TypeRef, ModuleRef, Method, TypeSpec or TypeDef tables; more precisely, a MemberRefParent coded index)

·              Name (index into String heap)

·              Signature (index into Blob heap)

An entry is made into the MemberRef table whenever a reference is made, in the CIL code, to a method or field which is defined in another module or assembly. (Also, an entry is made for a call to a method with a VARARG signature, even when it is defined in the same module as the callsite)

This contains informative text only

1.             Class shall be one of ...  [ERROR]

a.            a TypeRef token, if the class that defines the member is defined in another module.  (Note: it is unusual, but legal, to use a TypeRef token when the member is defined in this same module - its TypeDef token can be used instead)

b.            a ModuleRef token, if the member is defined, in another module of the same assembly, as a global function or variable

c.            a MethodDef token, when used to supply a call-site signature for a varargs method that is defined in this module.  The Name shall match the Name in the corresponding MethodDef row.  The Signature shall match the Signature in the target method definition  [ERROR]

d.            a TypeSpec token, if the member is a member of a constructed type

2.             Class shall not be null (this would indicate an unresolved reference to a global function or variable)  [ERROR]

3.             Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

4.             The Name string shall be a valid CLS identifier  [CLS]

5.             Signature shall index a valid field or method signature in the Blob heap.  In particular, it shall embed exactly one of the following ‘calling conventions’:  [ERROR]

a.            DEFAULT (0x0)

b.            VARARG (0x5)

c.            FIELD (0x6)

Implementation Specific (Microsoft)

The above names are defined in the file inc\CorHdr.h as part of the SDK using the prefix IMAGE_CEE_CS_CALLCONV_

6.             The MemberRef table shall contain no duplicates, where duplicate rows have the same Class, Name and Signature  [WARNING]

7.             Signature shall not have the VARARG (0x5) calling convention  [CLS]

8.             There shall be no duplicate rows, where Name fields are compared using CLS conflicting-identifier-rules  [CLS]

9.             There shall be no duplicate rows, where Name fields are compared using CLS conflicting-identifier-rules.  (note, particular, that the return type, and whether parameters are marked ELEMENT_TYPE_BYREF (see clause 22.1.15) are ignored in the CLS.  For example, int foo() and double foo() result in duplicate rows by CLS rules.  Similarly, void bar(int i) and void bar(int& i) also result in duplicate rows by CLS rules)  [CLS]

Implementation Specific (Microsoft)

Name shall not be of the form _VtblGapSequenceNumber<_CountOfSlots> -- such methods are dummies, used to pad entries in the vtable that CLI generates for COM interop.  Such methods cannot be called from managed or unmanaged code  [ERROR]

10.          If Class and Name resolve to a field, then that field shall not have a value of Compilercontrolled (see clause 22.1.5) in its Flags.FieldAccessMask subfield  [ERROR]

11.          If Class and Name resolve to a method, then that method shall not have a value of he Compilercontrolled  in its Flags.MemberAccessMask (see clause 22.1.9) subfield  [ERROR]

End informative text

 

21.24   Method : 0x06

The Method table has the following columns:

·              RVA (a 4 byte constant)

·              ImplFlags (a 2 byte bitmask of type MethodImplAttributes, clause 22.1.9)

·              Flags (a 2 byte bitmask of type MethodAttribute, clause 22.1.9)

·              Name (index into String heap)

·              Signature (index into Blob heap)

·              ParamList (index into Param table).  It marks the first of a contiguous run of Parameters owned by this method.  The run continues to the smaller of:

·              the last row of the Param table

·              the next run of Parameters, found by inspecting the ParamList of the next row in the Method  table

Conceptually, every row in the Method table is owned by one, and only one, row in the TypeDef table.

The rows in the Method table result from .method directives (see Chapter 14). The RVA column is computed when the image for the PE file is emitted and points to the COR_ILMETHOD structure for the body of the method (see Chapter 24.4)

This contains informative text only

1.             The Method table may contain zero or more rows

2.             Each row shall have one, and only one, owner row in the TypeDef table [ERROR]

3.             ImplFlags may have only those values set that are specified [ERROR]

4.             Flags may have only those values set that are specified  [ERROR]

5.             The MemberAccessMask (see clause 22.1.9) subfield of Flags shall contain precisely one of Compilercontrolled, Private, FamANDAssem, Assem, Family, FamORAssem, or Public [ERROR]

6.             The following combined bit settings in Flags are illegal  [ERROR]

a.            Static | Final

b.            Static | Virtual

c.            Static | NewSlot

d.            Final | Abstract

e.            Abstract | PinvokeImpl

f.             Compilercontrolled | Virtual

g.            Compilercontrolled | Final

h.            Compilercontrolled | SpecialName

i.              Compilercontrolled | RTSpecialName

7.             An abstract method shall be virtual.  So: if Flags.Abstract = 1 then Flags.Virtual shall also be 1  [ERROR]

8.             If Flags.RTSpecialName = 1 then Flags.SpecialName shall also be 1  [ERROR]

Implementation Specific (Microsoft)

An abstract method cannot have ForwardRef (see clause 22.1.10) set, and vice versa.  So:

if Flags.Abstract = 1 then ImplFlags.ForwardRef shall be 0  [ERROR]

if ImplFlags.ForwardRef = 1 then Flags.Abstract shall be 0 [ERROR]

The ForwardRef bit may be set only in an OBJ file (used by managed extensions for C++)  By the time  a method executes, its ForwardRef shall be 0  [ERROR]

9.             If Flags.HasSecurity = 1, then at least one of the following conditions shall be true:  [ERROR]

o             this Method owns at least row in the DeclSecurity table

o             this Method has a custom attribute called SuppressUnmanagedCodeSecurityAttribute

10.          If this Method owns one (or more) rows in the DeclSecurity table then Flags.HasSecurity shall be 1  [ERROR]

11.          If this Method has a custom attribute called SuppressUnmanagedCodeSecurityAttribute then Flags.HasSecurity shall be 1  [ERROR]

12.          A Method may have a custom attribute called DynamicSecurityMethodAttribute -  but this has no effect whatsoever upon the value of its Flags.HasSecurity

13.          Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

14.          Interfaces cannot have instance constructors.  So, if this Method is owned by an Interface, then its Name cannot be .ctor [ERROR]

15.          Interfaces can only own virtual methods (not static or instance methods).  So, if this Method is owned by an Interface, Flags.Static shall be clear  [ERROR]

16.          The Name string shall be a valid CLS identifier  (unless Flags.RTSpecialName is set - for example, .cctor is legal)   [CLS]

17.          Signature shall index a valid method signature in the Blob heap [ERROR]

18.          If Flags.Compilercontrolled = 1, then this row is ignored completely in duplicate checking

19.          If the owner of this method is the internally-generated type called <Module>, it denotes that this method is defined at module scope. ( In C++, the method is called global and can be referenced only within its compiland, from its point of declaration forwards.)  In this case:

a.            Flags.Static shall be 1  [ERROR]

b.            Flags.Abstract shall be 0  [ERROR]

c.            Flags.Virtual shall be 0  [ERROR]

d.            Flags.MemberAccessMask subfield shall be one of Compilercontrolled, Public, or Private [ERROR]

e.            module-scope methods are not allowed  [CLS]

20.          It makes no sense for ValueTypes, which have no identity, to have synchronized methods (unless they are boxed).  So, if the owner of this method is a ValueType then the method cannot be synchronized.  i.e. ImplFlags.Synchronized shall be 0  [ERROR]

21.          There shall be no duplicate rows in the Method table, based upon owner+Name+Signature (where owner is the owning row in the TypeDef table). (Note however that if Flags.Compilercontrolled = 1, then this row is completely excluded from duplicate checking)  [ERROR]

22.          There shall be no duplicate rows in the Method table, based upon owner+Name+Signature, where Name fields are compared using CLS conflicting-identifier-rules; also, the Type defined in the signatures shall be different.  So, for example, "int i" and "float i" would be considered CLS duplicates; also, the return type of the method is ignored  (Note however that if Flags.Compilercontrolled = 1, then this row is completely excluded from duplicate checking as explained above)  [CLS]

23.          If any of Final, NewSlot, HideBySig are set in Flags, then Flags.Virtual shall also be set  [ERROR]

24.          If Flags.PInvokeImpl is set, then Flags.Virtual shall be 0  [ERROR]

25.          If Flags.Abstract != 1 then exactly one of the following shall also be true:  [ERROR]

o             RVA != 0

o             Flags.PInvokeImpl = 1

o             ImplFlags.Runtime = 1

Implementation Specific (Microsoft)

There is an additional mutually exclusive possibility related to COM Interop: the owner of this method is marked Import = 1

26.          If the method is Compilercontrolled, then the RVA shall be non-zero or marked with PinvokeImpl = 1  [ERROR]

27.          Signature shall have exactly one of the following managed calling conventions  [ERROR]

a.            DEFAULT (0x0)

b.            VARARG (x5)

Implementation Specific (Microsoft)

The above names are defined in the file inc\CorHdr.h as part of the SDK, using a prefix of “IMAGE_CEE_CS_CALLCONV_

28.          Signature shall have the calling conventions DEFAULT (0x0). [CLS]

29.          Signature: If and only if the method is not Static then the calling convention byte in Signature has its HASTHIS (0x20) bit set  [ERROR]

30.          Signature: If the method is static, then the HASTHIS (0x20) bit in the calling convention byte shall be 0  [ERROR]

31.          If EXPLICITTHIS (0x40) in the signature is set, then HASTHIS (0x20) shall also be set  (note in passing: if EXPLICITTHIS is set, then the code is not verifiable)  [ERROR]

32.          The EXPLICITTHIS (0x40) bit can be set only in signatures for function pointers: signatures whose MethodDefSig is preceded by FNPTR (0x1B)  [ERROR]

33.          If RVA = 0, then either: [ERROR]

o             Flags.Abstract = 1, or

o             ImplFlags.Runtime = 1, or

o             Flags.PinvokeImpl = 1, or

Implementation Specific (Microsoft)

There is are two additional mutually exclusive possibilities:

ImplFlags.InternalCall = 1, or

owner row in TypeDef table has Flags.Import = 1

34.          If RVA != 0, then: [ERROR]

a.            Flags.Abstract shall be 0, and

b.            ImplFlags.CodeTypeMask shall be have exactly one of the following values: Native, CIL, or Runtime, and

c.            RVA shall point into the CIL code stream in this file

Implementation Specific (Microsoft)

There are two additional requirements:

ImplFlags.InternalCall = 0, and

owner row in TypeDef table has Flags.tdImport = 0

35.          If Flags.PinvokeImpl = 1 then  [ERROR]

o             RVA = 0 and the method owns a row in the ImplMap table, OR

Implementation Specific (Microsoft)

For IJW thunks there is an additional possibility, where the method is actually a managed method in the current module:

RVA != 0 and the method does not own a row in the ImplMap table and the method signature includes a custom modifier that specifies the native calling convention

36.          If Flags.RTSpecialName = 1 then Name shall be one of: [ERROR]

a.            .ctor (object constructor method)

b.            .cctor (class constructor method)

Implementation Specific (Microsoft)

For COM Interop, an additional class of method names are permitted:

 _VtblGap<SequenceNumber>_<CountOfSlots>

where <SequenceNumber> and <CountOfSlots> are decimal numbers

37.          Conversely, if Name is any of the above special names then Flags.RTSpecialName shall be set  [ERROR]

38.          If Name = .ctor (object constructor method) then:

a.            return type in Signature shall be ELEMENT_TYPE_VOID (see clause 22.1.15)  [ERROR]

b.            Flags.Static shall be 0  [ERROR]

c.            Flags.Abstract shall be 0  [ERROR]

d.            Flags.Virtual shall be 0  [ERROR]

e.             ‘Owner’ type shall be a valid Class or ValueType (not <Module> and not an Interface) in the TypeDef table  [ERROR]

f.             there can be 0 or more .ctors for any given ‘owner’

39.          If Name = .cctor (class constructor method) then:

a.            return type in Signature shall be ELEMENT_TYPE_VOID (see clause 22.1.15)   [ERROR]

b.            Signature shall have DEFAULT (0x0) for its calling convention [ERROR]

c.            there shall be no parameters supplied in Signature  [ERROR]

d.            Flags.Static shall be set  [ERROR]

e.            Flags.Virtual shall be clear  [ERROR]

f.             Flags.Abstract shall be clear  [ERROR]

40.          Among the set of methods owned by any given row in the TypeDef table there can be 0 or 1 methods named .cctor (never 2 or more)  [ERROR]

End informative text

21.25   MethodImpl : 0x19

MethodImpls let a compiler override the default inheritance rules provided by the CLI. Their original use was to allow a class “C”, that inherited method “Foo” from interfaces I and J, to provide implementations for both methods (rather than have only one slot for “Foo” in its vtable). But MethodImpls can be used for other reasons too, limited only by the compiler writer’s ingenuity within the constraints defined in the Validation rules below.

In the example above, Class specifies “C”, MethodDeclaration specifies I::Foo, MethodBody specifies the method which provides the implementation for I::Foo (either a method body within “C”, or a method body implemented by a superclass of “C”)

The MethodImpl table has the following columns:

·              Class (index into TypeDef table)

·              MethodBody (index into Method or MemberRef table; more precisely, a MethodDefOrRef coded index)

·              MethodDeclaration (index into Method or MemberRef table; more precisely, a MethodDefOrRef coded index)

ilasm uses the .override directive to specify the rows of the MethodImpl table (see clause 9.3.2).

This contains informative text only

1.             The MethodImpl table may contain zero or more rows

2.             Class shall index a valid row in the TypeDef table  [ERROR]

3.             MethodBody shall index a valid row in the Method or MethodRef table  [ERROR]

4.             The method indexed by MethodDeclaration shall have Flags.Virtual set  [ERROR]

5.             The owner Type of the method indexed by MethodDeclaration shall not have Flags.Sealed = 0  [ERROR]

6.             The method indexed by MethodBody shall be a member of Class or some superclass of Class (MethodImpls do not allow compilers to ‘hook’ arbitrary method bodies)  [ERROR]

7.             The method indexed by MethodBody shall be virtual  [ERROR]

8.             The method indexed by MethodBody shall have its Method.RVA != 0  (cannot be an unmanaged method reached via PInvoke, for example)  [ERROR]

9.             MethodDeclaration shall index a method in the ancestor chain of Class (reached via its Extends chain) or in the interface tree of Class (reached via its InterfaceImpl entries)  [ERROR]

10.          The method indexed by MethodDeclaration shall not be final (its Flags.Final shall be 0)  [ERROR]

11.          The method indexed by MethodDeclaration shall be accessible to Class [ERROR]

12.          The method signature defined by MethodBody shall match those defined by MethodDeclaration  [ERROR]

13.          There shall be no duplicate rows, based upon Class+MethodDeclaration [ERROR]

End informative text

21.26   MethodSemantics : 0x18

The MethodSemantics table has the following columns:

·              Semantics (a 2 byte bitmask of type MethodSemanticsAttributes, clause 22.1.10)

·              Method (index into the Method table)

·              Association (index into the Event or Property table; more precisely, a HasSemantics coded index)

The rows of the MethodSemantics table are filled by .property (see Chapter 16) and .event directives (see Chapter 17).   See clause 21.13 for more information.

This contains informative text only

1.             MethodSemantics table may contain zero or more rows

2.             Semantics may have only those values set that are specified [ERROR]

3.             Method shall index a valid row in the Method table, and that row shall be for a method defined on the same class as the Property or Event this row describes  [ERROR]

4.             All methods for a given Property or Event shall have the same accessibility (ie the MemberAccessMask subfield of their Flags row) and cannot be Compilercontrolled [CLS]

5.             Semantics: constrained as follows:

o             If this row is for a Property, then exactly one of Setter, Getter, or Other shall be set  [ERROR]

o             If this row is for an Event, then exactly one of AddOn, RemoveOn, Fire, or Other shall be set  [ERROR]

6.             If this row is for an Event, and its Semantics is Addon or RemoveOn, then the row in the Method table indexed by Method shall take a Delegate as a parameter, and return void  [ERROR]

7.             If this row is for an Event, and its Semantics is Fire, then the row indexed in the Method table by Method may return any type

Implementation Specific (Microsoft)

The Microsoft implementation limits the return type of the Fire method to void

8.             For each property, there shall be a setter, or a getter, or both [CLS]

9.             Any getter method for a property whose Name is xxx shall be called get_xxx  [CLS]

10.          Any setter method for a property whose Name is xxx shall be called set_xxx  [CLS]

11.          If a property provides both getter and setter methods, then these methods shall have the same value in the Flags.MemberAccessMask subfield [CLS]

12.          If a property provides both getter and setter methods, then these methods shall have the same value for their Method.Flags.Virtual  [CLS]

13.          Any getter and setter methods shall have Method.Flags.SpecialName = 1  [CLS]

14.          Any getter method shall have a return type which matches the signature indexed by the Property.Type field  [CLS]

15.          The last parameter for any setter method shall have a type which matches the signature indexed by the Property.Type field  [CLS]

16.          Any setter method shall have return type ELEMENT_TYPE_VOID (see clause 22.1.15) in Method.Signature  [CLS]

17.          If the property is indexed, the indexes for getter and setter shall agree in number and type  [CLS]

18.          Any AddOn method for an event whose Name is xxx shall have the signature: void add_xxx (<DelegateType> handler)  [CLS]

19.          Any RemoveOn method for an event whose Name is xxx shall have the signature: void remove_xxx(<DelegateType> handler)  [CLS]

20.          Any Fire method for an event whose Name is xxx shall have the signature: void raise_xxx(Event e) [CLS]

End informative text

21.27   Module : 0x00

The Module table has the following columns:

·              Generation (2 byte value, reserved, shall be zero)

·              Name (index into String heap)

·              Mvid  (index into Guid heap; simply a Guid used to distinguish between two versions of the same module)

·              EncId (index into Guid heap, reserved, shall be zero)

·              EncBaseId (index into Guid heap, reserved, shall be zero)

The Mvid column shall index a unique GUID in the GUID heap (see Section 23.2.5) that identifies this instance of the module.  The Mvid may be ignored on read by conforming implementations of the CLI. The Mvid should be newly generated for every module, using the algorithm specified in ISO/IEC 11578:1996 (Annex A) or another compatible algorithm.

Note: The term GUID stands for Globally Unique IDentifier, a 16-byte long number typically displayed using its hexadecimal encoding.  A GUID may be generated by several well-known algorithms including those used for UUIDs (Universally Unique IDentifiers) in RPC and CORBA, as well as CLSIDs, GUIDs, and IIDs in COM. 

 

Rationale: While the VES itself makes no use of the Mvid, other tools (such as debuggers, which are outside the scope of this standard) rely on the fact that the Mvid almost always differs from one module to another.

The Generation, EncId and EncBaseId columns can be written as zero, and can be ignored by conforming implementations of the CLI.   The rows in the Module table result from .module directives in the Assembly (see Section 6.4).

This contains informative text only

1.             The Module table shall contain one and only one row  [ERROR]

2.             Name shall index a non-null string.  This string should match exactly any corresponding ModuleRef.Name string that resolves to this module.  [ERROR]

Implementation Specific (Microsoft)

Name is limited to MAX_PATH_NAME

The format of Name is <file name>.<file extension> with no path or drive letter; on POSIX-compliant systems Name contains no colon, no forward-slash, no backslash.

3.             Mvid shall index a non-null GUID in the Guid heap  [ERROR]

End informative text

21.28   ModuleRef : 0x1A

The ModuleRef table has the following column:

·              Name (index into String heap)

The rows in the ModuleRef table result from .module extern directives in the Assembly (see Section 6.5).

This contains informative text only

1.             Name shall index a non-null string in the String heap.  This string shall enable the CLI to locate the target module (typically, it might name the file used to hold the module)  [ERROR]

Implementation Specific (Microsoft)

Name is limited to MAX_PATH_NAME

The format of Name is <filename>.<extension> (eg, “Foo.DLL” - no drive letter, no path); on POSIX-compliant systems Name contains no colon, no forward-slash, no backslash.

2.             There should be no duplicate rows  [WARNING]

3.             Name should match an entry in the Name column of the File table.  Moreover, that entry shall enable the CLI to locate the target module (typically it might name the file used to hold the module)  [ERROR]

End informative text

21.29   NestedClass : 0x29

The NestedClass table has the following columns:

·              NestedClass (index into the TypeDef table)

·              EnclosingClass (index into the TypeDef table)

The NestedClass table records which Type definitions are nested within which other Type definition.  In a typical high-level language, including ilasm, the nested class is defined as lexically ‘inside’ the text of its enclosing Type.

This contains informative text only

The NestedClass table records which Type definitions are nested within which other Type definition. In a typical high-level language, the nested class is defined as lexically ‘inside’ the text of its enclosing Type

1.             The NestedClass table may contain zero or more rows

2.             NestedClass shall index a valid row in the TypeDef table [ERROR]

3.             EnclosingClass shall index a valid row in the TypeDef table (note particularly, it is not allowed to index the TypeRef table) [ERROR]

4.             There should be no duplicate rows (ie same values for NestedClass and EnclosingClass)  [WARNING]

5.             A given Type can only be nested by one encloser.  So, there cannot be two rows with the same value for NestedClass, but different value for EnclosingClass  [ERROR]

6.             A given Type can ‘own’ several different nested Types, so it is perfectly legal to have two or more rows with the same value for EnclosingClass but different values for NestedClass

End informative text

21.30   Param : 0x08

The Param table has the following columns:

·              Flags (a 2  byte bitmask of type ParamAttributes, clause 22.1.12)

·              Sequence (a 2 byte constant)

·              Name (index into String heap)

Conceptually, every row in the Param table is owned by one, and only one, row in the Method table

The rows in the Param table result from the parameters in a method declaration (see Section 14.4), or from a .param attribute attached to a method (see clause 14.4.1).

This contains informative text only

1.             Param table may contain zero or more rows

2.             Each row shall have one, and only one, owner row in the MethodDef table  [ERROR]

3.             Flags may have only those values set that are specified (all combinations valid)  [ERROR]

4.             Sequence shall have a value >= 0 and <= number of parameters in owner method.  A Sequence value of 0 refers to the owner method’s return type; its parameters are then numbered from 1 onwards  [ERROR]

5.             Successive rows of the Param table that are owned by the same method shall be ordered by increasing Sequence value - although gaps in the sequence are allowed  [WARNING]

6.             If Flags.HasDefault = 1 then this row shall own exactly one row in the Constant table  [ERROR]

7.             If Flags.HasDefault = 0, then there shall be no rows in the Constant table owned by this row  [ERROR]

8.             parameters cannot be given default values, so Flags.HasDefault shall be 0  [CLS]

9.             if Flags.FieldMarshal = 1 then this row shall own exactly one row in the FieldMarshal table  [ERROR]

10.          Name may be null or non-null

11.          If Name is non-null, then it shall index a non-null string in the String heap  [WARNING]

Implementation Specific (Microsoft)

This string is limited to   

End informative text

21.31   Property : 0x17

Properties within metadata are best viewed as a means to gather together collections of methods defined on a class, give them a name, and not much else.  The methods are typically get_ and set_ methods, already defined on the class, and inserted like any other methods into the Method table.  The association is held together by three separate tables – see the below:

figure 5

Row 3 of the PropertyMap table indexes row 2 of the TypeDef table on the left (MyClass), whilst indexing row 4 of the Property table on the right – the row for a property called Foo.  This setup establishes that MyClass has a property called Foo.  But what methods in the Method table are gathered together as ‘belonging’ to property Foo?  That association is contained in the MethodSemantics table – its row 2 indexes property Foo to the right, and row 2 in the Method table to the left (a method called get_Foo).  Also, row 3 of the MethodSemantics table indexes Foo to the right, and row 3 in the Method table to the left (a method called set_Foo).  As the shading suggests, MyClass has another property, called Bar, with two methods, get_Bar and set_Bar.

Property tables do a little more than group together existing rows from other tables.  The Property table has columns for Flags, Name (eg Foo and Bar in  the example here) and Type. In addition, the MethodSemantics table has a column to record whether the method it points at is a set_, a get_ or other.

Note: The CLS (see Partition I_alink=Partition_I) refers to instance, virtual, and static properties.  The signature of a property (from the Type column) can be used to distinguish a static property, since instance and virtual properties will have the “HASTHIS” bit set in the signature (see clause 22.2.1) while a static property will not.  The distinction between an instance and a virtual property depends on the signature of the getter and setter methods, which the CLS requires to be either both virtual or both instance.

The Property ( 0x17 ) table has the following columns:

·              Flags (a 2 byte bitmask of type PropertyAttributes, clause 22.1.13)

·              Name (index into String heap)

·              Type (index into Blob heap)  [the name of this column is misleading.  It does not index a TypeDef or TypeRef table – instead it indexes the signature in the Blob heap of the Property)

This contains informative text only

1.             Property table may contain zero or more rows

2.             Each row shall have one, and only one, owner row in the PropertyMap table (as described above)  [ERROR]

3.             PropFlags may have only those values set that are specified (all combinations valid)  [ERROR]

4.             Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

5.             The Name string shall be a valid CLS identifier  [CLS]

6.             Type shall index a non-null signature in the Blob heap  [ERROR]

7.             The signature indexed by Type shall be a valid signature for a property (ie, low nibble of leading byte is 0x8).  Apart from this leading byte, the signature is the same as the property’s get_ method  [ERROR]

8.             Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name+Type  [ERROR]

9.             There shall be no duplicate rows based upon Name, where Name fields are compared using CLS conflicting-identifier-rules (in particular, properties cannot be overloaded by their Type – a class cannot have two properties, "int Foo" and "String Foo", for example)  [CLS]

End informative text

21.32   PropertyMap : 0x15

The PropertyMap table has the following columns:

·              Parent (index into the TypeDef table)

·              PropertyList (index into Property table).  It marks the first of a contiguous run of Properties owned by Parent.  The run continues to the smaller of:

·              the last row of the Property table

·              the next run of Properties, found by inspecting the PropertyList of the next row in this PropertyMap table

The PropertyMap and Property tables result from putting the .property directive on a class (see Chapter 16).

This contains informative text only

1.             PropertyMap table may contain zero or more rows

2.             There shall be no duplicate rows, based upon Parent (a given class has only one ‘pointer’ to the start of its property list)  [ERROR]

3.             There shall be no duplicate rows, based upon PropertyList (different classes cannot share rows in the Property table)  [ERROR]

End informative text

21.33   StandAloneSig : 0x11

Signatures are stored in the metadata Blob heap.  In most cases, they are indexed by a column in some table – Field.Signature, Method.Signature, MemberRef.Signature, etc.  However, there are two cases that require a metadata token for a signature that is not indexed by any metadata table.  The StandAloneSig table fulfils this need.  It has just one column, that points to a Signature in the Blob heap.

The signature shall describe either:

·              a method – code generators create a row in the StandAloneSig table for each occurrence of a calli CIL instruction.  That row indexes the call-site signature for the function pointer operand of the calli instruction

·              local variables – code generators create one row in the StandAloneSig table for each method, to describe all of its local variables.  The .locals directive in ilasm generates a row in the StandAloneSig table.

TheStandAloneSig table has the following column:

·              Signature (index into the Blob heap)

Example (informative):

// On encountering the calli instruction, ilasm generates a signature

// in the blob heap (DEFAULT, ParamCount = 1, RetType = int32, Param1 = int32),

// indexed by the StandAloneSig table:

 

.assembly Test {}

 

.method static int32 AddTen(int32)

{ ldarg.0

  ldc.i4  10

  add

  ret

}

 

.class Test

{ .method static void main()

  { .entrypoint

    ldc.i4.1

    ldftn int32 AddTen(int32)

    calli int32(int32)

    pop

    ret

  }

}

This contains informative text only

1.             The StandAloneSig table may contain zero or more rows

2.             Signature shall index a valid signature in the Blob heap  [ERROR]

3.             The signature 'blob' indexed by Signature shall be a valid METHOD or LOCALS signature  [ERROR]

4.             Duplicate rows are allowed

End informative text

21.34   TypeDef : 0x02

The TypeDef table has the following columns:

·              Flags (a 4 byte bitmask of type TypeAttributes, clause 22.1.14)

·              Name (index into String heap)

·              Namespace (index into String heap)

·              Extends (index into TypeDef, TypeRef or TypeSpec table; more precisely, a TypeDefOrRef coded index)

·              FieldList (index into Field table; it marks the first of a continguous run of Fields owned by this Type).  The run continues to the smaller of:

·              the last row of the Field table

·              the next run of Fields, found by inspecting the FieldList of the next row in this TypeDef table

·              MethodList (index into Method table; it marks the first of a continguous run of Methods owned by this Type).  The run continues to the smaller of:

·              the last row of the Method table

·              the next run of Methods, found by inspecting the MethodList of the next row in this TypeDef table

Note that any type shall be one, and only one, of

·              Class (Flags.Interface = 0, and derives ultimately from System.Object)

·              Interface (Flags.Interface = 1)

·              Value type, derived ultimately from System.ValueType

 For any given type, there are two separate, and quite distinct ‘inheritance’ chains of pointers to other types (the pointers are actually implemented as indexes into metadata tables).  The two chains are:

·              Extension chain – defined via the Extends column of the TypeDef table.  Typically, a derived Class extends a base Class (always one, and only one, base Class)

·              Interface chains – defined via the InterfaceImpl table. Typically, a Class implements zero, one or more Interfaces

These two chains (extension and interface) are always kept separate in metadata.  The Extends chain represents one-to-one relations – that is,  one Class extends  (or ‘derives from’) exactly one other Class (called its immediate base Class).  The Interface chains may represent one-to-many relations – that is,  one Class might well implement two or more Interfaces.

Example (informative, written in C#):

interface IA {void m1(int i);        }

interface IB {void m2(int i, int j); }

class C : IA, IB {

  int f1, f2;

  public void m1(int i)        {f1 = i;        }

  public void m2(int i, int j) {f1 = i; f2 = j;}

}

// In metadata, Interface IA extends nothing; Interface IB

// extends nothing; class C extends System.Object and implements

// Interfaces IA and IB

An Interface can also ‘inherit’ from one or more other Interfaces – metadata stores those links via the InterfaceImpl table (the nomenclature is a little inappropriate here – there is no “implementation” involved – perhaps a clearer name might have been Interface table, or InterfaceInherit table) 

Example (informative, written in C#):

interface IA          {void m1(int i);        }

interface IB          {void m2(int i, int j); }

interface IC : IA, IB {void m3(int i, int j, int k);}

class C : IC {

  int f1, f2, f3;

  public void m1(int i)               {f1 = i;                }

  public void m2(int i, int j)        {f1 = i; f2 = j;        }

  public void m3(int i, int j, int k) {f1 = i; f2 = j; f3 = k;}

}

// In metadata, Interface IA extends nothing; Interface IB extends

// nothing; Interface IC "inherits" Interfaces IA and IB (defined via

// the InterfaceImpl table); Class C extends System.Object and

// implements Interface IC (see InterfaceImpl table)

There are also a few specialized types.  One is the user-defined Enum – which shall derive directly from System.Enum (via the Extends field)

Another slightly specialized type is a nested type which is declared in ilasm as lexically nested within an enclosing type declaration.   Whether a type is nested can be determined by the value of its Flags.Visibility sub-field – it shall be one of the set {NestedPublic, NestedPrivate, NestedFamily, NestedAssembly, NestedFamANDAssem, NestedFamORAssem}.

The roots of the inheritance hierarchies look like this:

 

There is one system-defined root – System.Object.  All Classes and ValueTypes shall derive, ultimately, from System.Object; Classes can derive from other Classes (through a single, non-looping chain) to any depth required.  This Extends inheritance chain is shown with heavy arrows.

(See below for details of the System.Delegate Class)

Interfaces do not inherit from one another, however, they specify zero or more other interfaces which shall be implemented.  The Interface requirement chain is shown as light, dashed arrows.  This includes links between Interfaces and Classes/ValueTypes – where the latter are said to implement that interface or interfaces.

Regular ValueTypes (ie excluding Enums – see later) are defined as deriving directly from System.ValueType.  Regular ValueTypes cannot be derived to a depth of more than one.  (Another way to state this is that user-defined ValueTypes shall be sealed.)  User-defined Enums shall derive directly from System.Enum.  Enums cannot be derived to a depth of more than one below System.Enum.  (Another way to state this is that user-defined Enums shall be sealed.)  System.Enum derives directly from System.ValueType.

The hierarchy below System.Delegate is as follows:

 

User-defined delegates derive directly from System.MulticastDelegate. Delegates cannot be derived to a depth of more than one.

For the directives to declare types see Chapter 9.

This contains informative text only

1.             TypeDef table may contain one or more rows.  There is always one row (row zero) that represents the pseudo class that acts as parent for functions and variables defined at module scope.

2.             Flags:

a.            Flags may have only those values set that are specified  [ERROR]

b.            can set 0 or 1 of SequentialLayout and  ExplicitLayout (if none set, then defaults to AutoLayout)  [ERROR]

c.            can set 0 or 1 of UnicodeClass and AutoClass (if none set, then defaults to AnsiClass)  [ERROR]

Implementation Specific (Microsoft)

if RTSpecialName is set, then this Type is regarded as deleted (used in Edit&Continue and incremental compilation scenarios)  Perform no checks on this Type or any of its members (the information is not physically deleted; it is just ‘flagged’ as logically deleted)  Note: this situation can only be seen on in-memory metadata - it is not persisted to disk, and therefore irrelevant to checks done by an offline tool

if Import is set (denotes a Type defined via the TlbImp tool), then all the methods owned by this Type shall have their Method.RVA = 0  [ERROR]

d.            If Flags.HasSecurity = 1, then at least one of the following conditions shall be true:  [ERROR]

·              this Type owns at least one row in the DeclSecurity table

·              this Type has a custom attribute called SuppressUnmanagedCodeSecurityAttribute

e.            If this Type owns one (or more) rows in the DeclSecurity table then Flags.HasSecurity shall be 1  [ERROR]

f.             If this Type has a custom attribute called SuppressUnmanagedCodeSecurityAttribute then Flags.HasSecurity shall be 1  [ERROR]

g.            Note that it is legal for an Interface to have HasSecurity set.  However, the security system ignores any permission requests attached to that Interface

3.             Name shall index a non-null string  in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

4.             The Name string shall be a valid CLS identifier  [CLS]

5.             Namespace may be null or non-null

6.             If non-null, then Namespace shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME.  Also, the concatenated TypeNamespace + "." + TypeName shall be less than MAX_CLASS_NAME

7.             If non-null, Namespace’s string shall be a valid CLS Identifier [CLS]

8.             Every Class (with the sole exception of System.Object) shall extend one, and only one, other Class - so Extends for a Class shall be non-null [ERROR]

9.             System.Object shall have an Extends value of null  [ERROR]

10.          System.ValueType shall have an Extends value of System.Object  [ERROR]

11.          With the sole exception of System.Object, for any Class, Extends shall index a valid row in the TypeDef or TypeRef table, where valid means 1 <= row <= rowcount.  In addition, that row itself shall be a Class (not an Interface or ValueType)  In addition, that base Class shall not be sealed (its Flags.Sealed shall be 0)  [ERROR]

Implementation Specific (Microsoft)

Extends may index a row in the TypeSpec table - this is an extension for prototyping parametric-polymorphism. [WARNING]

12.          A Class cannot extend itself, or any of its children (ie its derived Classes), since this would introduce loops in the hierarchy tree  [ERROR]

13.          An Interface never extends another Type - so Extends shall be null (Interfaces do implement other Interfaces, but recall that this relationship is captured via the InterfaceImpl table, rather than the Extends column)  [ERROR]

14.          FieldList can be null or non-null

15.          A Class or Interface may ‘own’ zero or more fields

16.          A ValueType shall have a non-zero size - either by defining at least one field, or by providing a non-zero ClassSize  [ERROR]

17.          If FieldList is non-null, it shall index a valid row in the Field table, where valid means 1 <= row <= rowcount+1  [ERROR]

18.          MethodList can be null or non-null

19.          A Type may ‘own’ zero or more methods

20.          The runtime size of a ValueType shall not exceed 1 MByte (0x100000 bytes)  [ERROR]

Implementation Specific (Microsoft)

Current implementation actually allows 0x3F0000 bytes, but may be reduced in future

21.          If MethodList is non-null, it shall index a valid row in the Method table, where valid means 1 <= row <= rowcount+1  [ERROR]

22.          A Class which has one or more abstract methods cannot be instantiated, and shall have Flags.Abstract = 1. Note that the methods owned by the class include all of those inherited from its base class and interfaces it implements, plus those defined via its MethodList. (The CLI shall analyze class definitions at runtime; if it finds a class to have one or more abstract methods, but has Flags.Abstract = 0, it will throw an exception)  [ERROR]

23.          An Interface shall have Flags.Abstract = 1  [ERROR]

24.          It is legal for an abstract Type to have a constructor method (ie, a method named .ctor)

25.          Any non-abstract Type (ie Flags.Abstract = 0) shall provide an implementation (body) for every method its contract requires.  Its methods may be inherited from its base class, from the interfaces it implements, or defined by itself.  The implementations may be inherited from its base class, or defined by itself  [ERROR]

26.          An Interface (Flags.Interface == 1) can own static fields (Field.Static == 1) but cannot own instance fields (Field.Static == 0)  [ERROR]

27.          An Interface cannot be sealed (if Flags.Interface == 1, then Flags.Sealed shall be 0)  [ERROR]

28.          All of the methods owned by an Interface (Flags.Interface == 1) shall be abstract (Flags.Abstract == 1)  [ERROR]

29.          There shall be no duplicate rows in the TypeDef table, based on Namespace+Name (unless this is a nested type - see below)  [ERROR]

30.          If this is a nested type, there shall be no duplicate row in the TypeDef table, based upon Namespace+Name+OwnerRowInNestedClassTable  [ERROR]

31.          There shall be no duplicate rows, where Namespace+Name fields are compared using CLS conflicting-identifier-rules (unless this is a nested type - see below)  [CLS]

32.          If this is a nested type, there shall be no duplicate rows, based upon Namespace+Name+OwnerRowInNestedClassTable and where Namespace+Name fields are compared using CLS conflicting-identifier-rules  [CLS]

33.          If Extends = System.Enum  (ie, type is a user-defined Enum) then:

a.            shall be sealed (Sealed = 1)  [ERROR]

b.            shall not have any methods of its own (MethodList chain shall be zero length)  [ERROR]

c.            shall not implement any interfaces (no entries in InterfaceImpl table for this type)  [ERROR]

d.            shall not have any properties   [ERROR]

e.            shall not have any events   [ERROR]

f.             any static fields shall be literal (have Flags.Literal = 1) [ERROR]

g.            shall have at least one static, literal field.  If more than one, they shall all be of the same type.  Any such static literal fields shall be of the type of the Enum  [CLS]

h.            shall be at least one instance field, of integral type  [ERROR]

i.              shall be exactly one instance field  [CLS]

j.              the Name string of the instance field shall be "value__"; it shall marked RTSpecialName; its type shall be one of (see clause 22.1.15):   [CLS]

·              ELEMENT_TYPE_U1

·              ELEMENT_TYPE_I2

·              ELEMENT_TYPE_I4

·              ELEMENT_TYPE_I8

k.            shall be no other members (ie, apart from any static literals, and the one instance field called "value__" )  [CLS]

34.          A Nested type (defined above) shall own exactly one row in the NestedClass table - where ‘owns’ means a row in that NestedClass table whose NestedClass column holds the TypeDef token for this type definition  [ERROR]

35.          A ValueType shall be sealed  [ERROR]

End informative text

21.35   TypeRef : 0x01

The TypeRef table has the following columns:

·              ResolutionScope (index into Module, ModuleRef, AssemblyRef or TypeRef tables, or null; more precisely, a ResolutionScope coded index)

·              Name (index into String heap)

·              Namespace (index into String heap)

This contains informative text only

1.             ResolutionScope shall be exactly one of:

a.            null - in this case, there shall be a row in the ExportedType table for this Type - its Implementation field shall contain a File token or an AssemblyRef token that says where the type is defined [ERROR]

b.            a TypeRef token, if this is a nested type (which can be determined by, for example, inspecting the Flags column in its TypeDef table - the accessibility subfield is one of the tdNestedXXX set)  [ERROR]

c.            a ModuleRef token, if the target type is defined in another module within the same Assembly as this one [ERROR]

d.            a Module token, if the target type is defined in the current module - this should not occur in a CLI (“compressed metadata”) module [WARNING]

e.            an AssemblyRef token, if the target type is defined in a different Assembly from the current module [ERROR]

2.             Name shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME

3.             Namespace may be null, or non-null

4.             If non-null, Namespace shall index a non-null string in the String heap  [ERROR]

Implementation Specific (Microsoft)

This string is limited to MAX_CLASS_NAME.  Also, the concatenated TypeNamespace + "." + TypeName shall be less than MAX_CLASS_NAME

5.             The Name string shall be a valid CLS identifier  [CLS]

6.             There shall be no duplicate rows, where a duplicate has the same ResolutionScope, Name and Namespace  [ERROR]

7.             There shall be no duplicate rows, where Name and Namespace fields are compared using CLS conflicting-identifier-rules  [CLS]

End informative text

21.36   TypeSpec : 0x1B

The TypeSpec table has just one column, which indexes the specification of a Type, stored in the Blob heap.  This provides a metadata token for that Type (rather than simply an index into the Blob heap) – this is required, typically, for array operations – creating, or calling methods on the array class.

The TypeSpec table has the following column:

·              Signature (index into the Blob heap, where the blob is formatted as specified in clause 22.2.14)

Note that TypeSpec tokens can be used with any of the CIL instructions that take a TypeDef or TypeRef token – specifically:

castclass, cpobj, initobj, isinst, ldelema, ldobj, mkrefany, newarr, refanyval, sizeof, stobj, box, unbox

This contains informative text only

The TypeSpec table may contain zero or more rows

Signature shall index a valid Type specification in the Blob heap  [ERROR]

There shall be no duplicate rows, based upon Signature  [ERROR]

End informative text

22      Metadata Logical Format: Other Structures

22.1      Bitmasks and Flags

This section explains the various flags and bitmasks used in the various metadata tables.

22.1.1      Values for AssemblyHashAlgorithm

Algorithm

Value

None

0x0000

Reserved (MD5)                                        

0x8003

SHA1

0x8004

 

22.1.2      Values for AssemblyFlags

Flag

Value

Description

PublicKey

0x0001

The assembly reference holds the full (unhashed) public key.

SideBySideCompatible

0x0000

The assembly is side by side compatible

<reserved>

0x0030

Reserved: both bits shall be zero

EnableJITcompileTracking

0x8000

Reserved  (a conforming implementation of the CLI may ignore this setting on read; some implementations might use this bit to indicate that a CIL-to-native-code compiler should generate CIL-to-native code map)

DisableJITcompileOptimizer

0x4000

Reserved  (a conforming implementation of the CLI may ignore this setting on read; some implementations might use this bit to indicate that a CIL-to-native-code compiler should not generate optimized code)

 

22.1.3      Values for Culture

ar-SA

ar-IQ

ar-EG

ar-LY

ar-DZ

ar-MA

ar-TN

ar-OM

ar-YE

ar-SY

ar-JO

ar-LB

ar-KW

ar-AE

ar-BH

ar-QA

bg-BG

ca-ES

zh-TW

zh-CN

zh-HK

zh-SG

zh-MO

cs-CZ

da-DK

de-DE

de-CH

de-AT

de-LU

de-LI

el-GR

en-US

en-GB

en-AU

en-CA

en-NZ

en-IE

en-ZA

en-JM

en-CB

en-BZ

en-TT

en-ZW

en-PH

es-ES-Ts

es-MX

es-ES-Is

es-GT

es-CR

es-PA

es-DO

es-VE

es-CO

es-PE

es-AR

es-EC

es-CL

es-UY

es-PY

es-BO

es-SV

es-HN

es-NI

es-PR

Fi-FI

fr-FR

fr-BE

fr-CA

Fr-CH

fr-LU

fr-MC

he-IL

hu-HU

is-IS

it-IT

it-CH

Ja-JP

ko-KR

nl-NL

nl-BE

nb-NO

nn-NO

pl-PL

pt-BR

pt-PT

ro-RO

ru-RU

hr-HR

Lt-sr-SP

Cy-sr-SP

sk-SK

sq-AL

sv-SE

sv-FI

th-TH

tr-TR

ur-PK

id-ID

uk-UA

be-BY

sl-SI

et-EE

lv-LV

lt-LT

fa-IR

vi-VN

hy-AM

Lt-az-AZ

Cy-az-AZ

eu-ES

mk-MK

af-ZA

ka-GE

fo-FO

hi-IN

ms-MY

ms-BN

kk-KZ

ky-KZ

sw-KE

Lt-uz-UZ

Cy-uz-UZ

tt-TA

pa-IN

gu-IN

ta-IN

te-IN

kn-IN

mr-IN

sa-IN

mn-MN

gl-ES

kok-IN

syr-SY

div-MV

 

 

Note on RFC 1766 Locale names: a typical string would be “en-US”.  The first part (“en” in the example) uses ISO 639 characters (“Latin-alphabet characters in lowercase.  No diacritical marks of modified characters are used”).  The second part (“US” in the example) uses ISO 3166 characters (similar to ISO 639, but uppercase).  In other words, the familiar ASCII characters – a-z and A-Z respectively.  However, whilst RFC 1766 recommends the first part is lowercase, the second part uppercase, it allows mixed case. Therefore,  the validation rule checks only that Culture is one of the strings in the list above – but the check is totally case-blind – where case-blind is the familiar fold on values less than U+0080

22.1.4      Flags for Events [EventAttributes]

Flag

Value

Description

SpecialName

0x0200

Event is special.

RTSpecialName

0x0400

CLI provides 'special' behavior, depending upon the name of the event

 

22.1.5      Flags for Fields [FieldAttributes]

Flag

Value

Description

FieldAccessMask

0x0007

 

Compilercontrolled

0x0000

Member not referenceable

Private

0x0001

Accessible only by the parent type

FamANDAssem

0x0002

Accessible by sub-types only in this Assembly

Assembly        

0x0003

Accessibly by anyone in the Assembly

Family          

0x0004

Accessible only by type and sub-types

FamORAssem      

0x0005

Accessibly by sub-types anywhere, plus anyone in assembly

Public          

0x0006

Accessibly by anyone who has visibility to

this scope field contract attributes

Static          

0x0010

Defined on type, else per instance

InitOnly        

0x0020

Field may only be initialized, not written to after init

Literal         

0x0040

Value is compile time constant

NotSerialized   

0x0080

Field does not have to be serialized when type is remoted

SpecialName     

0x0200

Field is special

Interop Attributes

PInvokeImpl     

0x2000

Implementation is forwarded through PInvoke.

Additional flags

RTSpecialName

0x0400

CLI provides 'special' behavior, depending upon the name of the field

HasFieldMarshal          

0x1000

Field has marshalling information

HasDefault               

0x8000

Field has default

HasFieldRVA              

0x0100

Field has RVA

 

22.1.6      Flags for Files [FileAttributes]

Flag

Value

Description

ContainsMetaData

0x0000

This is not a resource file

ContainsNoMetaData

0x0001

This is a resource file or other non-metadata-containing file

 

22.1.7      Flags for ImplMap [PInvokeAttributes]

Flag

Value

Description

NoMangle

0x0001

PInvoke is to use the member name as specified

Character set

CharSetMask

0x0006

This is a resource file or other non-metadata-containing file

CharSetNotSpec

0x0000

 

CharSetAnsi

0x0002

 

CharSetUnicode

0x0004

 

CharSetAuto

0x0006

 

SupportsLastError

0x0040

Information about target function. Not relevant for fields

Calling convention

CallConvMask

0x0700

 

CallConvWinapi

0x0100

 

CallConvCdecl

0x0200

 

CallConvStdcall

0x0300

 

CallConvThiscall

0x0400

 

CallConvFastcall

0x0500

 

 

22.1.8      Flags for ManifestResource [ManifestResourceAttributes]

Flag

Value

Description

VisibilityMask

0x0007

 

Public

0x0001

The Resource is exported from the Assembly

Private

0x0002

The Resource is private to the Assembly

 

22.1.9      Flags for Methods [MethodAttributes]

 

Flag

Value

Description

MemberAccessMask

0x0007

 

Compilercontrolled

0x0000

Member not referenceable

Private

0x0001

Accessible only by the parent type

FamANDAssem

0x0002

Accessible by sub-types only in this Assembly

Assem

0x0003

Accessibly by anyone in the Assembly

Family

0x0004

Accessible only by type and sub-types

FamORAssem

0x0005

Accessibly by sub-types anywhere, plus anyone in assembly

Public

0x0006

Accessibly by anyone who has visibility to this scope

 

Static

0x0010

Defined on type, else per instance

Final

0x0020

Method may not be overridden

Virtual

0x0040

Method is virtual

HideBySig

0x0080

Method hides by name+sig, else just by name

 

VtableLayoutMask

0x0100

Use this mask to retrieve vtable attributes

ReuseSlot

0x0000

Method reuses existing slot in vtable

NewSlot

0x0100

Method always gets a new slot in the vtable

 

Abstract

0x0400

Method does not provide an implementation

SpecialName

0x0800

Method is special

Interop attributes

PInvokeImpl

0x2000

Implementation is forwarded through PInvoke

UnmanagedExport

0x0008

Reserved: shall be zero for conforming implementations

Additional flags

RTSpecialName

0x1000

CLI provides 'special' behavior, depending upon the name of the method

HasSecurity

0x4000

Method has security associate with it

RequireSecObject

0x8000

Method calls another method containing security code.

 

Implementation Specific (Microsoft)

UnmanagedExport indicates a managed method exported via thunk to unmanaged code

22.1.10    Flags for Methods [MethodImplAttributes]

Flag

Value

Description

CodeTypeMask

0x0003

 

IL

0x0000

Method impl is CIL

Native

0x0001

Method impl is native

OPTIL

0x0002

Reserved: shall be zero in conforming implementations

Runtime

0x0003

Method impl is provided by the runtime

 

ManagedMask

0x0004

Flags specifying whether the code is managed or unmanaged.

Unmanaged

0x0004

Method impl is unmanaged, otherwise managed

Managed

0x0000

Method impl is managed

Implementation info and interop

ForwardRef

0x0010

Indicates method is defined; used primarily in merge scenarios

PreserveSig

0x0080

Reserved: conforming implementations may ignore

InternalCall

0x1000

Reserved: shall be zero in conforming implementations

Synchronized

0x0020

Method is single threaded through the body

NoInlining

0x0008

Method may not be inlined

MaxMethodImplVal

0xffff

Range check value   

 

Implementation Specific (Microsoft)

PreserveSig method signature is not to be mangled to do HRESULT conversion. InternalCall indicates the method body is provided by the.

22.1.11    Flags for MethodSemantics [MethodSemanticsAttributes]

Flag

Value

Description

Setter

0x0001

Setter for property 

Getter

0x0002

Getter for property

Other

0x0004

Other method for property or event  

AddOn

0x0008

AddOn method for event

RemoveOn

0x0010

RemoveOn method for event   

Fire

0x0020

Fire method for event

 

22.1.12    Flags for Params [ParamAttributes]

Flag

Value

Description

In

0x0001

Param is [In]   

Out

0x0002

Param is [out]  

Optional

0x0004

Param is optional   

HasDefault

0x1000

Param has default value

HasFieldMarshal

0x2000

Param has FieldMarshal

Unused

0xcfe0

Reserved: shall be zero in a conforming implementation

 

22.1.13    Flags for Properties [PropertyAttributes]

Flag

Value

Description

SpecialName

0x0200

Property is special

RTSpecialName

0x0400

Runtime(metadata internal APIs) should check name encoding

HasDefault

0x1000

Property has default

Unused

0xe9ff

Reserved: shall be zero in a conforming implementation

 

22.1.14    Flags for Types [TypeAttributes]

Flag

Value

Description

Visibility attributes

VisibilityMask    

0x00000007

Use this mask to retrieve visibility information

NotPublic         

0x00000000

Class has no public scope

Public            

0x00000001

Class has public scope

NestedPublic      

0x00000002

Class is nested with public visibility

NestedPrivate     

0x00000003

Class is nested with private visibility

NestedFamily      

0x00000004

Class is nested with family visibility

NestedAssembly    

0x00000005

Class is nested with assembly visibility

NestedFamANDAssem 

0x00000006

Class is nested with family and assembly visibility

NestedFamORAssem  

0x00000007

Class is nested with family or assembly visibility

Class layout attributes

LayoutMask        

0x00000018

Use this mask to retrieve class layout information

AutoLayout        

0x00000000

Class fields are auto-laid out

SequentialLayout  

0x00000008

Class fields are laid out sequentially

ExplicitLayout    

0x00000010

Layout is supplied explicitly

Class semantics attributes

ClassSemanticsMask   

0x00000020

Use this mask to retrive class semantics information

Class                

0x00000000

Type is a class

Interface            

0x00000020

Type is an interface

Special semantics in addition to class semantics

Abstract     

0x00000080

Class is abstract

Sealed       

0x00000100

Class cannot be extended

SpecialName       

0x00000400

Class name is special

Implementation Attributes

Import               

0x00001000

Class/Interface is imported

Serializable         

0x00002000

Class is serializable

String formatting Attributes

StringFormatMask     

0x00030000

Use this mask to retrieve string information for native interop

AnsiClass            

0x00000000

LPSTR is interpreted as ANSI

UnicodeClass         

0x00010000

LPSTR is interpreted as Unicode

AutoClass            

0x00020000

LPSTR is interpreted automatically

Class Initialization Attributes

BeforeFieldInit      

0x00100000

Initialize the class before first static field access

Additional Flags

RTSpecialName   

0x00000800

CLI provides 'special' behavior, depending upon the name of the Type

HasSecurity     

0x00040000

Type has security associate with it

 

22.1.15    Element Types used in Signatures

The following table lists the values for ELEMENT_TYPE constants. These are used extensively in metadata signature blobs – see Section 22.2

Implementation Specific (Microsoft)

These values are defined in the file inc\CorHdr.h in the SDK

Name

Value

Remarks

ELEMENT_TYPE_END

0x00

Marks end of a list

ELEMENT_TYPE_VOID

0x01

 

ELEMENT_TYPE_BOOLEAN

0x02

 

ELEMENT_TYPE_CHAR

0x03

 

ELEMENT_TYPE_I1

0x04

 

ELEMENT_TYPE_U1

0x05

 

ELEMENT_TYPE_I2

0x06

 

ELEMENT_TYPE_U2

0x07

 

ELEMENT_TYPE_I4

0x08

 

ELEMENT_TYPE_U4

0x09

 

ELEMENT_TYPE_I8

0x0a

 

ELEMENT_TYPE_U8

0x0b

 

ELEMENT_TYPE_R4

0x0c

 

ELEMENT_TYPE_R8

0x0d

 

ELEMENT_TYPE_STRING

0x0e

 

ELEMENT_TYPE_PTR  

0x0f

Followed by <type> token

ELEMENT_TYPE_BYREF

0x10

Followed by <type> token

ELEMENT_TYPE_VALUETYPE

0x11

Followed by <type> token

ELEMENT_TYPE_CLASS

0x12

Followed by <type> token

ELEMENT_TYPE_ARRAY

0x14

<type> <rank> <boundsCount> <bound1> … <loCount> <lo1> …

ELEMENT_TYPE_TYPEDBYREF

0x16

 

ELEMENT_TYPE_I

0x18

System.IntPtr

ELEMENT_TYPE_U

0x19

System.UIntPtr

ELEMENT_TYPE_FNPTR

0x1b

Followed by full method signature

ELEMENT_TYPE_OBJECT

0x1c

System.Object

ELEMENT_TYPE_SZARRAY

0x1d

Single-dim array with 0 lower bound

ELEMENT_TYPE_CMOD_REQD

0x1f

Required modifier : followed by a TypeDef or TypeRef token

ELEMENT_TYPE_CMOD_OPT

0x20

Optional modifier : followed by a TypeDef or TypeRef token

ELEMENT_TYPE_INTERNAL

0x21

Implemented within the CLI

 

 

 

ELEMENT_TYPE_MODIFIER

0x40

Or’d with following element types

ELEMENT_TYPE_SENTINEL

0x41

Sentinel for varargs method signature

ELEMENT_TYPE_PINNED

0x45

Denotes a local variable that points at a pinned object

22.2      Blobs and Signatures

The word signature is conventionally used to describe the type info for a function or method – that is,  the type of each of its parameters, and the type of its return value.  Within metadata, the word signature is also used to describe the type info for fields, properties, and local variables.  Each Signature is stored as a (counted) byte array in the Blob heap.  There are five kinds of Signature, as follows:

·              MethodRefSig – differs from a MethodDefSig only for VARARG calls

·              MethodDefSig

·              FieldSig

·              PropertySig

·              LocalVarSig

·              TypeSpec

 The value of the leading byte of a Signature 'blob' indicates what kind of Signature it is. This section defines the binary 'blob' format for each kind of Signature. .  In the syntax diagrams that accompany many of the definitions, shading is used to combine what would otherwise be multiple diagrams into a single diagram; the accompanying text describes the use of shading.

Note that Signatures are compressed before being stored into the Blob heap (described below) by compressing the integers embedded in the signature.  The maximum encodable integer is 29 bits long, 0x1FFFFFFF. The compression algorithm used is as follows (bit 0 is the least significant bit):

·              If the value lies between 0 (0x00) and 127 (0x7F), inclusive, encode as a one-byte integer (bit #7 is clear, value held in bits #6 through #0)

·              If the value lies between 2^8 (0x80) and 2^14 – 1 (0x3FFF), inclusive, encode as a two-byte integer with bit #15 set, bit #14 clear (value held in bits #13 through #0)

·              Otherwise, encode as a 4-byte integer, with bit #31 set, bit #30 set, bit #29 clear (value held in bits #28 through #0)

·              A null string should be represented with the reserved single byte 0xFF, and no following data

Note: The table below shows several examples. The first column gives a value, expressed in familiar (C-like) hex notation . The second column shows the corresponding, compressed result, as it would appear in a PE file, with successive bytes of the result lying at successively higher byte offsets within the file.  (This is the opposite order from how regular binary integers are laid out in a PE file) 

Original Value

Compressed Representation

0x03

03

0x7F

7F (7 bits set)

0x80

8080

0x2E57

AE57

0x3FFF

BFFF

0x4000

C000 4000

0x1FFF FFFF

DFFF FFFF

Thus,  the most significant bits (the first ones encountered in a PE file) of a “compressed” field, can reveal whether it occupies 1, 2, or 4 bytes, as well as its value.  For this to work, the “compressed” value, as explained above, is stored in big-endian order - with the most significant byte at the smallest offset within the file.

Signatures make extensive use of constant values called ELEMENT_TYPE_xxx – see Clause 22.1.15.  In particular, signatures include two modifiers called:

ELEMENT_TYPE_BYREF – this element is a managed pointer (see Partition I_alink=Partition_I). This modifier can only occur in the definition of Param (clause 22.2.10) or RetType (clause 22.2.11).  It shall not occur within the definition of a Field (clause 22.2.4)

ELEMENT_TYPE_PTR – this element is an unmanaged pointer (see Partition I_alink=Partition_I). This modifier can occur in the definition of Param (clause 22.2.10) or RetType (clause 22.2.11) or Field (clause 22.2.4)

22.2.1      MethodDefSig

A MethodDefSig is indexed by the Method.Signature column.  It captures the signature of a method or global function.  The syntax chart for a MethodDefSig is:

 

This chart uses the following abbreviations:

HASTHIS = 0x20, used to encode the keyword instance in the calling convention, see Section 14.3

EXPLICITTHIS = 0x40, used to encode the keyword explicit in the calling convention, see Section 14.3

DEFAULT = 0x0, used to encode the keyword default in the calling convention, see Section 14.3

VARARG = for 0x5, used to encode the keyword vararg in the calling convention, see Section 14.3

Implementation Specific (Microsoft)

The above names are defined in the file inc\CorHdr.h as part of the SDK, using a prefix of “IMAGE_CEE_CS_CALLCONV_

The first byte of the Signature holds bits for HASTHIS, EXPLICITTHIS and calling convention – DEFAULT or VARARG. These are OR’d together.

ParamCount is an integer that holds the number of parameters (0 or more).  It can be any number between 0 and 0x1FFFFFFF  The compiler compresses it too (see Partition II Metadata Validation) – before storing into the 'blob' (ParamCount counts just the method parameters – it does not include the method’s return type)

The RetType item describes the type of the method’s return value (see clause 22.2.11)

The Param item describes the type of each of the method’s parameters.  There shall be ParamCount instances of the Param item (see clause 22.2.10).

22.2.2      MethodRefSig

A MethodRefSig is indexed by the MemberRef.Signature column.  This provides the callsite Signature for a method.  Normally, this callsite Signature shall match exactly the Signature specified in the definition of the target method.  For example, if a method Foo is defined that takes two uint32s and returns void; then any callsite shall index a signature that takes exactly two uint32s and returns void.  In this case, the syntax chart for a MethodRefSig is identical with that for a MethodDefSig – see clause 22.2.1

The Signature at a callsite differs from that at its definition, only for a method with the VARARG calling convention.  In this case, the callsite Signature is extended to include info about the extra VARARG arguments (for example, corresponding to the “...” in C syntax).  The syntax chart for this case is:

This chart uses the following abbreviations:

HASTHIS = 0x20, used to encode the keyword instance in the calling convention, see Section 14.3

EXPLICITTHIS = 0x40, used to encode the keyword explicit in the calling convention, see Section 14.3

DEFAULT = 0x0, used to encode the keyword default in the calling convention, see Section 14.3

VARARG = for 0x5, used to encode the keyword vararg in the calling convention, see Section 14.3

SENTINEL = 0x41 (see clause 22.1.15), used to encode “...” in the parameter list, see Section 14.3

Implementation Specific (Microsoft)

The above names are defined in the file inc\CorHdr.h as part of the SDK, using a prefix of “IMAGE_CEE_CS_CALLCONV_”.

·              The first byte of the Signature holds bits for HASTHIS, EXPLICITTHIS and calling convention – DEFAULT, VARARG, C, STDCALL, THISCALL, or FASTCALL. These are OR’d together.

·              ParamCount is an integer that holds the number of parameters (0 or more).  It can be any number between 0 and 0x1FFFFFFF  The compiler compresses it too (see Partition II Metadata Validation) – before storing into the 'blob' (ParamCount counts just the method parameters – it does not include the method’s return type)

·              The RetType item describes the type of the method’s return value (see clause 22.2.11)

·              The Param item describes the type of each of the method’s parameters.  There shall be ParamCount instances of the Param item (see clause 22.2.10).

The Param item describes the type of each of the method’s parameters.  There shall be ParamCount instances of the Param item.This starts just like the MethodDefSig for a VARARG method (see clause 22.2.1).  But then a SENTINEL token is appended, followed by extra Param items to describe the extra VARARG arguments.  Note that the ParamCount item shall  indicate the total number of Param items in the Signature – before and after the SENTINEL byte (0x41). 

In the unusual case that a callsite supplies no extra arguments, the signature shall not include a SENTINEL (this is the route shown by the lower arrow that bypasses SENTINEL and goes to the end of the MethodRefSig definition)

22.2.3      StandAloneMethodSig

A StandAloneMethodSig is indexed by the StandAloneSig.Signature column.  It is typically created as preparation for executing a calli instruction.  It is similar to a MethodRefSig, in that it represents a callsite signature, but its calling convention may specify an unmanaged target (the calli instruction invokes either managed, or unmanaged code).  Its syntax chart is:

This chart uses the following abbreviations (see Section 14.3):

HASTHIS for 0x20

EXPLICITTHIS for 0x40

DEFAULT  for 0x0

VARARG   for 0x5

C for 0x1

STDCALL for 0x2

THISCALL for 0x3

FASTCALL for 0x4

SENTINEL for  0x41 (see clause 22.1.15 and Section 14.3)

Implementation Specific (Microsoft)

The above names are defined in the file inc\CorHdr.h as part of the SDK, using a prefix of “IMAGE_CEE_CS_CALLCONV_

·              The first byte of the Signature holds bits for HASTHIS, EXPLICITTHIS and calling convention – DEFAULT, VARARG, C, STDCALL, THISCALL, or FASTCALL. These are OR’d together.

·              ParamCount is an integer that holds the number of parameters (0 or more).  It can be any number between 0 and 0x1FFFFFFF  The compiler compresses it too (see Partition II Metadata Validation) – before storing into the blob (ParamCount counts just the method parameters – it does not include the method’s return type)

·              The RetType item describes the type of the method’s return value (see clause 22.2.11)

·              The Param item describes the type of each of the method’s parameters.  There shall be ParamCount instances of the Param item (see clause 22.2.10).

This is the most complex of the various method signatures.   Two separate charts have been combined into one in this diagram, using shading to distinguish between them.  Thus, for the following calling conventions: DEFAULT (managed), STDCALL, THISCALL and FASTCALL (unmanaged), the signature ends just before the SENTINEL item (these are all non vararg signatures).  However, for the managed and unmanaged vararg calling conventions:

VARARG (managed) and C (unmanaged), the signature can include the SENTINEL and final Param items (they are not required, however).   These options are  indicated by the shading of boxes in the syntax chart.

22.2.4      FieldSig

A FieldSig is indexed by the Field.Signature column, or by the MemberRef.Signature column (in the case where it specifies a reference to a field, not a method, of course).   The Signature captures the field’s definition.  The field may be a static or instance field in a class, or it may be a global variable.  The syntax chart for a FieldSig looks like this:

This chart uses the following abbreviations:

FIELD for 0x6

Implementation Specific (Microsoft)

IMAGE_CEE_CS_CALLCONV_FIELD is defined in the file inc\CorHdr.h as part of the SDK.

CustomMod is defined in clause 22.2.7Type is defined in clause 22.2.12

22.2.5      PropertySig

A PropertySig is indexed by the Property.Type column.  It captures the type information for a Property – essentially, the signature of its getter method:

how many parameters are supplied to its getter method

the base type of the Property – the type returned by its getter method

type information for each parameter in the getter method – that is,  the index parameters

Note that the signatures of getter and setter are related precisely as follows:

·              The types of a getter’s  paramCount parameters are exactly the same as the first paramCount parameters of the setter

·              The return type of a getter is exactly the same as the type of the last parameter supplied to the setter

The syntax chart for a PropertySig looks like this:

This chart uses the following abbreviations:

PROPERTY for 0x8

Implementation Specific (Microsoft)

IMAGE_CEE_CS_CALLCONV_PROPERTY is defined in the file inc\CorHdr.h as part of the SDK.

Type specifies the type returned by the Getter method for this property.  Type is defined in clause 22.2.12Param is defined in clause 22.2.10.

ParamCount is an integer that holds the number of index parameters in the getter methods (0 or more).  (See clause 22.2.1)  (ParamCount counts just the method parameters – it does not include the method’s base type of the Property)

22.2.6      LocalVarSig

A LocalVarSig is indexed by the StandAloneSig.Signature column. It captures the type of all the local variables in a method.  Its syntax chart is:

This chart uses the following abbreviations:

LOCAL_SIG for 0x7, used for the .locals directive, see clause 14.4.1.3

Implementation Specific (Microsoft)

IMAGE_CEE_CS_CALLCONV_LOCAL_SIG is defined in the file inc\CorHdr.h as part of the SDK.

BYREF for ELEMENT_TYPE_BYREF (see clause 22.1.15)

Constraint is defined in clause 22.2.9.

Type is defined in clause 22.2.12

Count is an unsigned integer that holds the number of local variables.  It can be any number between 1 and 0xFFFE. 

There shall be Count instances of the Type in the LocalVarSig

22.2.7      CustomMod

The CustomMod (custom modifier) item in Signatures has a syntax chart like this:

This chart uses the following abbreviations:

CMOD_OPT                  for                           ELEMENT_TYPE_CMOD_OPT (see clause 22.1.15)

CMOD_REQD                                for                           ELEMENT_TYPE_CMOD_REQD (see clause 22.1.15)

The CMOD_OPT or CMOD_REQD value is compressed, see Section 22.2.

The CMOD_OPT or CMOD_REQD is followed by a metadata token that indexes a row in the TypeDef table or the TypeRef table.  However, these tokens are encoded and compressed – see clause 22.2.8 for details

If the CustomModifier is tagged CMOD_OPT, then any importing compiler can freely ignore it entirely.  Conversely, if the CustomModifier is tagged CMOD_REQD, any importing compiler shall ‘understand’ the semantic implied by this CustomModifier in order to reference the surrounding Signature.

Implementation Specific (Microsoft)

A typical use for a CustomModifier is for VISUAL C++ .NET to denote a method parameter as const. It does this using a CMOD_OPT, followed by a TypeRef to Microsoft.VisualC.IsConstModifier (defined in Microsoft.VisualC.DLL)

VISUAL C++ .NET also uses a CustomModifier (embedded within a RetType – see clause 22.2.11) to mark the native calling convention of a function.  Of course, if that routine is implemented as managed code, this info is not used.  But if it turns out to be implemented as unmanaged code, it becomes crucial, so that automatically generated thunks marshal the arguments correctly.  This technique is used in IJW (“It Just Works”) scenarios.  Strictly speaking, such a custom modifier does not apply only to the RetType, it really applies to the whole function.  In these cases, the TypeRef following the CMOD_OPT is to one of CallConvCdecl, CallConvStdcall, CallConvThiscall or CallConvFastcall.

22.2.8      TypeDefOrRefEncoded

These items are compact ways to store a TypeDef or TypeRef token in a Signature (see clause 22.2.12).

Consider a regular TypeRef token, such as 0x01000012.  The top byte of 0x01 indicates that this is a TypeRef token (see Partition V_alink=Partition_V for a list of the supported metadata token types).  The lower 3 bytes (0x000012) index row number 0x12 in the TypeRef table.

The encoded version of this TypeRef token is made up as follows:

1.             encode the table that this token indexes as the least significant 2 bits.  The bit values to use are 0, 1 and 2, specifying the target table is the TypeDef, TypeRef or TypeSpec table, respectively 

2.             shift the 3-byte row index (0x000012 in  this example) left by 2 bits and OR into the 2-bit encoding from step 1

3.             compress the resulting value (see Section 22.2).   This example yields the following encoded value:

a)  encoded = value for TypeRef table = 0x01 (from 1. above)

b)  encoded = ( 0x000012 << 2 ) |  0x01

            = 0x48 | 0x01

            = 0x49

c)  encoded = Compress (0x49)

            = 0x49

So, instead of the original, regular TypeRef token value of 0x01000012, requiring 4 bytes of space in the Signature 'blob',  this TypeRef token is encoded as a single byte.

22.2.9      Constraint

The Constraint item in Signatures currently has only one possible value – ELEMENT_TYPE_PINNED (see clause 22.1.15), which specifies that the target type is pinned in the runtime heap, and will not be moved by the actions of garbage collection. 

A Constraint can only be applied within a LocalVarSig (not a FieldSig).  The Type of the local variable shall either be a reference type (in other words, it points to the actual variable – for example, an Object, or a String); or it shall include the BYREF item.  The reason is that local variables are allocated on the runtime stack – they are never allocated from the runtime heap; so unless the local variable points at an object allocated in the GC heap, pinning makes no sense.

22.2.10    Param

The Param (parameter) item in Signatures has this syntax chart:

This chart uses the following abbreviations:

BYREF           for    0x10 (See clause 22.1.15)

TYPEDBYREF      for    0x16 (See clause 22.1.15)

CustomMod is defined in clause 22.2.7Type is defined in clause 22.2.12

22.2.11    RetType

The RetType (return type) item in Signatures has this syntax chart:

RetType is identical to Param except for one extra possibility, that it can include the type VOID.  This chart uses the following abbreviations:

BYREF           for    ELEMENT_TYPE_BYREF (see clause 22.1.15)

TYPEDBYREF      for    ELEMENT_TYPE_TYPEDBYREF (see clause 22.1.15)

VOID            for    ELEMENT_TYPE_VOID (see clause 22.1.15)

22.2.12    Type

Type is encoded in signatures as follows (I1 is an abbreviation for ELEMENT_TYPE_I1, etc., see clause 22.1.15):

Type ::=  

BOOLEAN | CHAR | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I  | U |

| VALUETYPE TypeDefOrRefEncoded

| CLASS TypeDefOrRefEncoded

| STRING

| OBJECT

| PTR CustomMod* VOID

| PTR CustomMod* Type

| FNPTR MethodDefSig

| FNPTR MethodRefSig

| ARRAY Type ArrayShape  (general array, see clause 22.2.13)

| SZARRAY CustomMod* Type (single dimensional, zero-based array i.e. vector)

22.2.13    ArrayShape

An ArrayShape has the following syntax chart:

Rank is an integer (stored in compressed form, see Section 22.2) that specifies the number of dimensions in the array (shall be 1 or more).  NumSizes is a compressed integer that says how many dimensions have specified sizes (it shall be 0 or more).  Size is a compressed integer specifying the size of that dimension – the sequence starts at the first dimension, and goes on for a total of NumSizes items. Similarly, NumLoBounds is a compressed integer that says how many dimensions have specified lower bounds (it shall be 0 or more). And LoBound is a compressed integer specifying the lower bound of that dimension – the sequence starts at the first dimension, and goes on for a total of NumLoBounds items.    None of the dimensions in these two sequences can be skipped, but the number of specified dimensions can be less than Rank.

Here are a few examples, all for element type int32:

 

Type

Rank

NumSizes

Size

NumLoBounds

LoBound

[0...2]

I4

1

1

3

0

 

[,,,,,,]

I4

7

0

 

0

 

[0...3, 0...2,,,,]

I4

6

2

4  3

2

0  0

[1...2, 6...8]

I4

2

2

2  3

2

1  6

[5, 3...5, , ]

I4

4

2

5  3

2

0  3

 

Note: definitions can nest, since the Type may itself be an array

22.2.14    TypeSpec

The signature in the Blob heap indexed by a TypeSpec token has the following format –

TypeSpecBlob :==

  PTR      CustomMod*  VOID

| PTR      CustomMod*  Type

| FNPTR    MethodDefSig

| FNPTR    MethodRefSig

| ARRAY    Type  ArrayShape

| SZARRAY  CustomMod*  Type

For compactness, the ELEMENT_TYPE_ prefixes  have been omitted from this list.  So, for example, “PTR” is shorthand for ELEMENT_TYPE_PTR. (see clause 22.1.15)  Note that a TypeSpecBlob does not begin with a calling-convention byte, so it differs from the various other signatures that are stored into Metadata.

22.2.15    Short Form Signatures

The general specification for signatures leaves some leeway in how to encode certain items.  For example, it appears legal to encode a String as either

long-form:    ( ELEMENT_TYPE_CLASS, TypeRef-to-System.String )

short-form:   ELEMENT_TYPE_STRING

Only the short form is valid.  The following table shows which short-forms should be used in place of each long-form item.  (As usual, for compactness, the ELEMENT_TYPE_ prefix have been omitted here – so VALUETYPE is short for ELEMENT_TYPE_VALUETYPE)

Long Form

Short Form

Prefix

TypeRef to:

 

CLASS

System.String

STRING

CLASS

System.Object

OBJECT

VALUETYPE

System.Void

VOID

VALUETYPE

System.Boolean

BOOLEAN

VALUETYPE

System.Char

CHAR

VALUETYPE

System.Byte

U1

VALUETYPE

System.Sbyte

I1

VALUETYPE

System.Int16

I2

VALUETYPE

System.UInt16

U2

VALUETYPE

System.Int32

I4

VALUETYPE

System.UInt32

U4

VALUETYPE

System.Int64

I8

VALUETYPE

System.UInt64

U8

VALUETYPE

System.IntPtr

I

VALUETYPE

System.UIntPtr

U

VALUETYPE

System.TypedReference

TYPEDBYREF

 

Note: arrays shall be encoded in signatures using one of ELEMENT_TYPE_ARRAY or ELEMENT_TYPE_SZARRAY. There is no long form involving a TypeRef to System.Array

22.3      Custom Attributes

A Custom Attribute has the following syntax chart:

All binary values are stored in little-endian format (except PackedLen items – used only as counts for the number of bytes to follow in a UTF8 string)

CustomAttrib starts with a Prolog – an unsigned int16, with value 0x0001

Next comes a description of the fixed arguments for the constructor method.  Their number and type is found by examining that constructor’s MethodDef; this info is not repeated in the CustomAttrib itself.  As the syntax chart shows, there can be zero or more FixedArgs. (note that VARARG constructor methods are not allowed in the definition of Custom Attributes)

Next is a description of the optional “named” fields and properties.  This starts with NumNamed – an unsigned int16 giving the number of “named” properties or fields that follow.  Note that NumNamed shall always be present.  If its value is zero, there are no “named” properties or fields to follow (and of course, in this case, the CustomAttrib shall end immediately after NumNamed)  In the case where NumNamed is non-zero, it is followed by NumNamed repeats of NamedArgs

The format for each FixedArg depends upon whether that argument is single, or an SZARRAY – this is shown in the upper and lower paths, respectively, of the syntax chart.  So each FixedArg is either a single Elem, or NumElem repeats of Elem.

(SZARRAY is the single byte 0x1d, and denotes a vector – a single-dimension array with a lower bound of zero)

NumElem is an unsigned int32 specifying the number of elements in the SZARRAY

 

An Elem takes one of three forms:

·              if the parameter kind is simple (bool, char, float32, float64, int8, int16, int32, int64, unsigned int8, unsigned int16, unsigned int32 or unsigned int64) then the 'blob' contains its binary value (Val). This pattern is also used if the parameter kind is an enum -- simply store the value of the enum's underlying integer type

·              if the parameter kind is string or type, then the blob contains a SerString – a PackedLen count of bytes, followed by the UTF8 characters.  (a type is stored as a string giving the full name of that type)

·              if the parameter kind is a boxed simple value type (bool, char, float32, float64, int8, int16, int32, int64, unsigned int8, unsigned int16, unsigned int32 or unsigned int64) then the blob contains the value type's FieldOrPropType (see below), followed by its binary value (Val).

Val is the binary value for a simple type.  A bool is a single byte with value 0 (false) or 1 (true); char is a two-byte unicode character; and the others have their obvious meaning..

 

A NamedArg is simply a FixedArg (discussed above) preceded by information to identify which field or property it represents.

FIELD is the single byte 0x53

PROPERTY is the single byte 0x54

The FieldOrPropType shall be exactly one of: ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR, ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2, ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8, ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, ELEMENT_TYPE_STRING or the constant 0x50 (for an argument of type System.Type).  See clause 22.1.15.

The FieldOrPropName is the name of the field or property, stored as a SerString (defined above).

The SerString used to encode an argument of type Type includes the full type name, followed optionally by the assembly where it is defined, its version, culture and public key token.  If the assembly name is omitted, the CLI looks first in this assembly, and then the assembly named mscorlib.

For example, consider the Type string “Ozzy.OutBack.Kangaroo+Wallaby, MyAssembly” for a class “Wallaby” nested within class “Ozzy.OutBack.Kangaroo”, defined in the assembly “MyAssembly”.

22.4      Marshalling Descriptors

A Marshalling Descriptor is like a signature – it’s a 'blob' of binary data.  It describes how a field or parameter (which, as usual, covers the method return, as parameter number 0) should be marshalled when calling to or from unmanaged code via PInvoke dispatch.  The ilasm syntax marshal can be used to create a marshalling descriptor, as can the pseudo custom attribute MarshalAsAttribute -- see clause 20.2.1)

Note that a conforming implementation of the CLI need only support marshalling of the types specified earlier – see clause 14.5.5.

Marshalling descriptors make use of constants named NATIVE_TYPE_xxx.  Their names and values are listed in the following table:

Name

Value

NATIVE_TYPE_BOOLEAN

0x02

NATIVE_TYPE_I1

0x03

NATIVE_TYPE_U1

0x04

NATIVE_TYPE_I2

0x05

NATIVE_TYPE_U2

0x06

NATIVE_TYPE_I4

0x07

NATIVE_TYPE_U4

0x08

NATIVE_TYPE_I8

0x09

NATIVE_TYPE_U8

0x0a

NATIVE_TYPE_R4

0x0b

NATIVE_TYPE_R8

0x0c

NATIVE_TYPE_LPSTR

0x14

NATIVE_TYPE_INT

0x1f

NATIVE_TYPE_UINT

0x20

NATIVE_TYPE_FUNC

0x26

NATIVE_TYPE_ARRAY

0x2a

 

Implementation Specific (Microsoft)

The Microsoft implementation supports a richer set of types to describe marshalling between Windows native types and COM.  These additional options are listed in the following table:

Implementation Specific (Microsoft)

Name

Value

Remarks

NATIVE_TYPE_CURRENCY

0x0f

 

NATIVE_TYPE_BSTR

0x13

 

NATIVE_TYPE_LPWSTR

0x15

 

NATIVE_TYPE_LPTSTR

0x16

 

NATIVE_TYPE_FIXEDSYSSTRING

0x17

 

NATIVE_TYPE_IUNKNOWN

0x19

 

NATIVE_TYPE_IDISPATCH

0x1a

 

NATIVE_TYPE_STRUCT

0x1b

 

NATIVE_TYPE_INTF

0x1c

 

NATIVE_TYPE_SAFEARRAY

0x1d

 

NATIVE_TYPE_FIXEDARRAY

0x1e

 

NATIVE_TYPE_BYVALSTR

0x22

 

NATIVE_TYPE_ANSIBSTR

0x23

 

NATIVE_TYPE_TBSTR

0x24

Selects BSTR or ANSIBSTR depending on platform

NATIVE_TYPE_VARIANTBOOL

0x25

2-byte Boolean value: false = 0; true = -1

NATIVE_TYPE_ASANY

0x28

 

NATIVE_TYPE_LPSTRUCT

0x2b

 

NATIVE_TYPE_CUSTOMMARSHALER

0x2c

Custom marshaler native type.  Shall be followed by a string in the format: "Native type name/0Custom marshaler type name/0Optional cookie/0"  OR  // "{Native type GUID}/0Custom marshaler type name/0Optional cookie/0"

NATIVE_TYPE_ERROR

0x2d

This native type coupled with ELEMENT_TYPE_I4 will map to VT_HRESULT

NATIVE_TYPE_MAX

0X50

Used to indicate “no info”

 

The 'blob' has the following format –

MarshalSpec ::=

  NativeInstrinsic

| ARRAY ArrayElemType ParamNum ElemMult NumElem

Implementation Specific (Microsoft)

The Microsoft implementation supports a wider range of options:

MarshalSpec ::=

   NativeIntrinsic

 | ARRAY ArrayElemType ParamNum ElemMult NumElem

 | CUSTOMMARSHALLER Guid UnmanagedType ManagedType Cookie

   |   FIXEDARRAY NumElem ArrayElemType

 | SAFEARRAY SafeArrayElemType

 

NativeInstrinsic ::= 

  BOOLEAN | I1 | U1 | I2 | U2 | I4 | U4 | I8 | U8 | R4 | R8

| CURRENCY | BSTR | LPSTR | LPWSTR | LPTSTR

| INT | UINT | FUNC | LPVOID

For compactness, the NATIVE_TYPE_ prefixes have been omitted in the above lists.  So, for example, “ARRAY” is shorthand for NATIVE_TYPE_ARRAY

Implementation Specific (Microsoft)

NativeIntrinsic ::= …

| FIXEDSYSSTRING | STRUCT | INTF | FIXEDARRAY | BYVALSTR | ANSIBSTR |

| TBSTR | VARIANTBOOL | ASANY | LPSTRUCT | ERROR

Guid is a counted-UTF8 string – e.g. “{90883F05-3D28-11D2-8F17-00A0C9A6186D}” – it shall include leading { and trailing } and be exactly 38 characters long

UnmanagedType is a counted-UTF8 string – e.g. “Point”

ManagedType is a counted-UTF8 string – e.g. “System.Util.MyGeometry” – it shall be the fully-qualified name (namespace and name) of a managed Type defined within the current assembly (that Type shall implement ICustomMarshaller, and provides a “to” and “from” marshalling method)

Cookie is a counted-UTF8 string – e.g. “123” – an empty string is allowed

NumElem is an integer (compressed as described in Section 22.2) that specifies how many elements are in the array

ArrayElemType :==

   NativeInstrinsic | BOOLEAN | I1 | U1 | I2 | U2

|  I4 | U4 | I8 | U8 | R4 | R8 | LPSTR | INT | UINT | FUNC | LPVOID

Implementation Specific (Microsoft)

ArrayElemType ::= …

| BSTR | LPWSTR | LPTSTR | FIXEDSYSSTRING | STRUCT | INTF | BYVALSTR

| ANSIBSTR | TBSTR | VARIANTBOOL | ASANY | LPSTRUCT | ERROR | MAX

The value MAX is used to indicate “no info”

The following information and table are specific to the Microsoft implementation of the CLI:

SafeArrayElemType ::=  

I2 | I4 | R4 | R8 | CY | DATE | BSTR | DISPATCH |

| ERROR | BOOL | VARIANT | UNKNOWN | DECIMAL | I1 | UI1 | UI2

| UI4 | INT | UINT

where each is prefixed by VT_. The values for the VT_xxx constants are given in the following table:

Implementation Specific (Microsoft)

Constant

Value

VT_I2

= 2,

VT_I4

= 3,

VT_R4

= 4,

VT_R8

= 5,

VT_CY

= 6,

VT_DATE

= 7,

VT_BSTR

= 8,

VT_DISPATCH

= 9,

VT_ERROR

= 10,

VT_BOOL

= 11,

VT_VARIANT

= 12,

VT_UNKNOWN

= 13,

VT_DECIMAL

= 14,

VT_I1

= 16,

VT_UI1

= 17,

VT_UI2

= 18,

VT_UI4

= 19,

VT_INT

= 22,

VT_UINT

= 23,

 

ParamNum is an integer (compressed as described in Section 22.2) specifying the parameter in the method call that provides the number of elements in the array – see below

ElemMult is an integer compressed as described in Section 22.2 (says by what factor to multiply – see below)

Note:

For example, in the method declaration:

Foo (int ar1[], int size1, byte ar2[], int size2)

The ar1 parameter might own a row in the FieldMarshal table, which indexes a MarshalSpec in the Blob heap with the format:

ARRAY  MAX  2 1  0

This says the parameter is marshalled to a NATIVE_TYPE_ARRAY.  There is no additional info about the type of each element (signified by that NATIVE_TYPE_MAX). The value of ParamNum is 2, which  indicates that parameter number 2 in the method (the one called “size1”) will  specify the number of elements in the actual array – let’s suppose its value on a particular call is 42.  The value of ElemMult is 1.  The value of NumElem is 0.  The calculated total size, in bytes, of the array is given by the formula:

if ParamNum == 0

SizeInBytes = NumElem * sizeof (elem)

else

SizeInBytes = ( @ParamNum * ElemMult  +  NumElem ) * sizeof (elem)

endif

 The syntax “@ParamNum” is used here to denote the value passed in for parameter number ParamNum – it would be 42 in this example.  The size of each element is calculated from the metadata for the ar1 parameter in Foo’s signature – an ELEMENT_TYPE_I4 (see clause 22.1.15) of size 4 bytes.

23      Metadata Physical Layout

The physical on-disk representation of metadata is a direct reflection of the logical representation described in Chapter 21_21_Metedata_Logical_Format_Tables and Chapter 22. That is, data is stored in streams representating the meta data tables and heaps. The main complication is that, where the logical representation is abstracted from the number of bytes needed for indexing into tables and columns, the physical representation has to take care of that explicitly by defining how to map logical metadata heaps and tables into their physical representations.

23.1      Fixed Fields

Complete CLI components (metadata and CIL instructions) are stored in a subset of the current Portable Executable (PE) File Format (see Chapter 24).  Because of this heritage, some of the fields in the physical representation of metadata have fixed values. When writing these fields they shall be set to the value indicated, on reading they may be ignored.

23.2      File Headers

23.2.1      Metadata root

The root of the physical metadata starts with a magic signature, several bytes of version and other miscellaneous information, followed by a count and an array of stream headers, one for each stream that is present. The actual encoded tables and heaps are stored in the streams, which immediately follow this array of headers.

Offset

Size

Field

Description

0

4

Signature

Magic signature for physical metadata : 0x424A5342.

4

2

MajorVersion

Major version, 1 (ignore on read)

6

2

MinorVersion

Minor version, 0 (ignore on read)

8

4

Reserved

Reserved, always 0 (see Section 23.1).

12

4

Length

Length of version string in bytes, say m.       

16

m

Version

UTF8-encoded version string of length m (ignore on read)

16+m

 

 

Padding to next 4 byte boundary, say x.

x

2

Flags

Reserved, always 0 (see Section 23.1).

x+2

2

Streams

Number of streams, say n.

x+4

 

StreamHeaders

Array of n StreamHdr structures.

 

23.2.2      Stream Header

A stream header gives the names, and the position and length of a particular table or heap. Note that the length of a Stream header structure is not fixed, but depends on the length of its name field (a variable length null-terminated string).

Offset

Size

Field

Description

0

4

Offset

Memory offset to start of this stream from start of the metadata root (see clause 23.2.1)

4

4

Size

Size of this stream in bytes, shall be a multiple of 4.

8

 

Name

Name of the stream as null terminated variable length array of ASCII characters, padded with \0 characters

 

Both logical tables and heaps are stored in streams.  There are five possible kinds of streams. A stream header with name “#Strings” that points to the physical representation of the string heap where identifier strings are stored; a stream header with name “#US” that points to the physical representation of the user string heap; a stream header with name “#Blob” that points to the physical representation of the blob heap, a stream header with name “#GUID” that points to the physical representation of the GUID heap; and a stream header with name “#~” that points to the physical representation of a set of tables.  (see Chapter 22)

Implementation Specific (Microsoft Only)

Some compilers store metadata in a #- stream, which holds an uncompressed, or non-optimized, representation of metadata tables; this includes extra metadata “pointer” tables.  Such PE files do not form part of this ECMA standard

Each kind of stream may occur at most once, that is, a meta-data file may not contain two “#US” streams, or five “#Blob” streams. Streams need not be there if they are empty.

The next sections will describe the structure of each kind of stream in more detail.

23.2.3      #Strings heap

The stream of bytes pointed to by a “#Strings” header is the physical representation of the logical string heap. The physical heap may contain garbage, that is, it may contain parts that are unreachable from any of the tables, but parts that are reachable from a table shall contain a valid null terminated UTF8 string. When the #String heap is present, the first entry is always the empty string (ie \0).

23.2.4      #US and #Blob heaps

The stream of bytes pointed to by a “#US” or “#Blob” header are the physical representation of logical Userstring and 'blob' heaps respectively. Both these heaps may contain garbage, as long as any part that is reachable from any of the tables contains a valid 'blob'. Individual blobs are stored with their length encoded in the first few bytes:

·              If the first one byte of the 'blob' is 0bs, then the rest of the 'blob' contains the (bs) bytes of actual data.

·              If the first two bytes of the 'blob' are 10bs and x, then the rest of the 'blob' contains the  (bs << 8 + x) bytes of actual data.

·              If the first four bytes of the 'blob' are 110bs, x, y, and z, then the rest of the 'blob' contains the (bs << 24 + x << 16 + y << 8 + z) bytes of actual data.

The first entry in both these heap is the empty 'blob' that consists of the single byte 0x00.

23.2.5      #GUID heap

The “#GUID” header points to a sequence of 128-bit GUIDs. There might be unreachable GUIDs stored in the stream.

23.2.6      #~ stream

The “#~” streams contain the actual physical representations of the logical metadata tables (see Chapter 21_21_Metedata_Logical_Format_Tables).  A  “#~” stream has the following top-level structure:

Offset

Size

Field

Description

0

4

Reserved

Reserved, always 0 (see Section 23.1).

4

1

MajorVersion

Major version of table schemata, always 1 (see Section 23.1).

5

1

MinorVersion

Minor version of table schemata, always 0 (see Section 23.1).

6

1

HeapSizes

Bit vector for heap sizes.

7

1

Reserved

Reserved, always 1 (see Section 23.1).

8

8

Valid

Bit vector of present tables, let n be the number of bits that are 1.

16

8

Sorted

Bit vector of sorted tables.

24

4*n

Rows

Array of n four byte unsigned integers indicating the number of rows for each present table.

24+4*n

 

Tables

The sequence of physical tables.

 

The HeapSizes field is a bitvector that encodes how wide indexes into the various heaps are.  If bit 0 is set, indexes into the “#String” heap are 4 bytes wide; if bit 1 is set, indexes into the “#GUID” heap are 4 bytes wide; bit 2 is not used; if bit 3 is set, indexes into the “#Blob” heap are 4 bytes wide.  Conversely, if the HeapSize bit for a particular heap is not set, indexes into that heap are 2 bytes wide.

Bit position

Description

0x01

Size of “#String” stream >= 2^16.

0x02

Size of “#GUID” stream >= 2^16

0x04

Size of “#Blob” stream >= 2^16.

 

The Valid field is a 64 bits wide bitvector that has a specific bit set for each table that is stored in the stream; the mapping of tables to indexes is given at the start of Chapter 21_21_Metedata_Logical_Format_Tables. For example when the DeclSecurity table is present in the logical metadata, bit 0x0e should be set in the Valid vector. It is illegal to include non-existent tables in Valid, so all bits above 0x2b shall be zero.

The Rows array contains the number of rows for each of the tables that are present. When decoding physical metadata to logical metadata, the number of 1’s in Valid indicates the number of elements in the Rows array.

A crucial aspect in the encoding of a logical table is its schema. The schema for each table is given in Chapter 21_21_Metedata_Logical_Format_Tables. For example, the table with assigned index 0x02 is a TypeDef  table, which, according to its specification in Section 21.34, has the following columns: 4 byte-wide flags, index into the String heap, another index into String heap, index into TypeDef or TypeRef table, index into Field table, index into Method table.

The physical representation of a table with schema (C0,…,Cn-1) with n rows consists of the concatenation of the physical representation of each of its rows. The physical representation of a row with schema  (C0,…,Cn-1) is the concatenation of the physical representation of each of its elements. The physical representation of a row cell e at a column with type C is defined as follows:

·              If e is a constant, it is stored using the number of bytes as specified for its column type C (i.e. a 2 byte bitmask of type PropertyAttributes)

·              If e is an index into the GUID heap, 'blob', or String heap, it is stored using the number of bytes as defined in the HeapSizes field.

·               If e is a simple index into a table with index i, it is stored using 2 bytes if table i has less than 2^16 rows, otherwise it is stored using 4 bytes.

·              If  e is a coded index (see clause 23.2.7) that points into table ti out of n possible tables t0, …tn-1, then it is stored as e << (log n) | tag{ t0, …tn-1}[ ti] using 2 bytes if the maximum number of rows of tables t0, …tn-1, is less than 2^16 – (log n), and using 4 bytes otherwise. The family of finite maps tag{ t0, …tn-1} is defined below. Note that decoding a physical row requires the inverse of this mapping. [For example, the Parent column of the Constant table indexes a row in the Field, Param or Property tables.  The actual table is encoded into the low 2 bits of the number, using the values: 0 => Field, 1 => Param,2 => Property.The remaining bits hold the actual row number being indexed.  For example, a value of 0x321, indexes row number 0xC8 in the Param table.]

TypeDefOrRef: 2 bits to encode tag

Tag

TypeDef

0

TypeRef

1

TypeSpec

2

 

HasConstant: 2 bits to encode tag

Tag

FieldDef

0

ParamDef

1

Property

2

 

HasCustomattribute: 5 bits to encode tag

Tag

MethodDef

0

FieldDef

1

TypeRef

2

TypeDef

3

ParamDef

4

InterfaceImpl

5

MemberRef

6

Module

7

DeclSecurity

8

Property

9

Event

10

Signature

11

ModuleRef

12

TypeSpec

13

Assembly

14

AssemblyRef

15

File

16

ExportedType

17

ManifestResource

18

 

HasFieldMarshall: 1 bit to encode tag

Tag

FieldDef

0

ParamDef

1

 

HasDeclSecurity: 2 bits to encode tag

Tag

TypeDef

0

MethodDef

1

Assembly

2

 

MemberRefParent: 3 bits to encode tag

Tag

Not used

0

TypeRef

1

ModuleRef

2

MethodDef

3

TypeSpec

4

 

HasSemantics: 1 bit to encode tag

Tag

Event

0

Property

1

 

MethodDefOrRef: 1 bit to encode tag

Tag

MethodDef

0

MemberRef

1

 

MemberForwarded: 1 bit to encode tag

Tag

FieldDef

0

MethodDef

1

 

Implementation: 2 bits to encode tag

Tag

File

0

AssemblyRef

1

ExportedType

 

 

CustomAttributeType: 3 bits to encode tag

Tag

Not used

0

Not used

1

MethodDef

2

MemberRef

3

Not used

4

 

ResolutionScope: 3 bits to encode tag

Tag

Module

0

ModuleRef

1

AssemblyRef

3

TypeRef

4

 

23.2.7      Coded Indexes

24      File Format Extensions to PE

This contains informative text only

The file format for CLI components is a strict extension of the current Portable Executable (PE) File Format. This extended PE  format enables the operating system to recognize runtime images, accommodates code emitted as CIL or native code, and accommodates runtime metadata as an integral part of the emitted code.    There are also specifications for a subset of the full Windows PE/COFF file format, in sufficient detail that a tool or compiler can use the specifications to emit valid CLI images.

The PE format frequently uses the term RVA (Relative Virtual Address). An RVA is the address of an item once loaded into memory, with the base address of the image file subtracted from it (i.e. the offset from the base address where the file is loaded). The RVA of an item will almost always differ from its position within the file on disk. To compute the file position of an item with RVA r, search all the sections in the PE file to find the section with RVA s, length l and file position p in which the RVA lies, ie s £ r < s+l. The file position of the item is then given by p+(r-s).

End informative text

24.1      Structure of the Runtime File Format

The figure below provides a high-level view of the CLI file format.  All runtime images contain the following:

·              PE headers, with specific guidelines on how field values should be set in a runtime file.

·              A CLI header that contains all of the runtime specific data entries. The runtime header is read-only and shall be placed in any read-only section.

·              The sections that contain the actual data as described by the headers, including imports/exports, data, and code.

The CLI header (see clause 24.3.3) is found using CLI Header directory entry in the PE header .  The CLI header in turn contains the address and sizes of the runtime data (metadata see Chapter 23 and CIL see Chapter 24.4) in the rest of the image.  Note that the runtime data can be merged into other areas of the PE format with the other data based on the attributes of the sections (such as read only versus execute, etc.).

24.2      PE Headers

A PE image starts with an MS-DOS header followed by a PE signature, followed by the PE file header, and then the PE optional header followed by PE section headers.

24.2.1      MS-DOS Header

The PE format starts with an MS-DOS stub of exactly the following 128 bytes to be placed at the front of the module. At offset 0x3c in the DOS header is a 4 byte unsigned integer offset lfanew to the PE signature (shall be “PE\0\0”), immediately followed by the PE file header. 

0x4d

0x5a

0x90

0x00

0x03

0x00

0x04

0x00

0x00

0x00

0x00

0x00

0xFF

0xFF

0x00

0x00

0x8b

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x40

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

0x00

lfanew

0x0e

0x1f

0xba

0x0e

0x00

0xb4

0x09

0xcd

0x21

0xb8

0x01

0x4c

0xcd

0x21

0x54

0x68

0x69

0x73

0x20

0x70

0x72

0x6f

0x67

0x72

0x61

0x6d

0x20

0x63

0x61

0x6e

0x6e

0x6f

0x74

0x20

0x62

0x65

0x20

0x72

0x75

0x6e

0x20

0x69

0x6e

0x20

0x44

0x4f

0x53

0x20

0x6d

0x6f

0x64

0x65

0x2e

0x0d

0x0d

0x0a

0x24

0x00

0x00

0x00

0x00

0x00

0x00

0x00

 

24.2.2      PE File Header

Immediately after the PE signature is the PE File header consisting of the following:

Offset

Size

Field

Description

0

2

Machine

Always 0x14c (see Section 23.1).

2

2

Number of Sections

Number of sections; indicates size of the Section Table, which immediately follows the headers.

4

4

Time/Date Stamp

Time and date the file was created in seconds since January 1st 1970 00:00:00 or 0.

8

4

Pointer to Symbol Table

Always 0 (see Section 23.1).

12

4

Number of Symbols

Always 0 (see Section 23.1).

16

2

Optional Header Size

Size of the optional header, the format is described below.

18

2

Characteristics

Flags indicating attributes of the file, see Characteristics.

 

24.2.2.1       Characteristics

A CIL-only DLL sets flag 0x2000 to 1, while an CIL only .exe has flag 0x2000 set to zero:

Flag

Value

Description

IMAGE_FILE_DLL

0x2000

The image file is a dynamic-link library (DLL).

 

Except for the IMAGE_FILE_DLL flag (0x2000), flags 0x0002, 0x0004, 0x008, 0x0100 and 0x0020 shall all be set, while all others shall always be zero (see Section 23.1).

24.2.3      PE Optional Header

Immediately after the PE Header is the PE Optional Header. This header contains the following information:

Offset

Size

Header part

Description

0

28

Standard fields

These define general properties of the PE file, see 24.2.3.1.

28

68

NT-specific fields

These include additional fields to support specific features of Windows, see 24.2.3.2.

96

128

Data directories

These fields are address/size pairs for special tables, found in the image file (for example, Import Table and Export Table).

 

24.2.3.1       PE Header Standard Fields

These fields are required for all PE files and contain the following information:

Offset

Size

Field

Description

0

2

Magic

Always 0x10B (see Section 23.1).

2

1

LMajor

Always 6 (see Section 23.1).

3

1

LMinor

Always 0 (see Section 23.1).

4

4

Code Size

Size of the code (text) section, or the sum of all code sections if there are multiple sections.

8

4

Initialized Data Size

Size of the initialized data section, or the sum of all such sections if there are multiple data sections.

12

4

Uninitialized Data Size

Size of the uninitialized data section, or the sum of all such sections if there are multiple unitinitalized data sections.

16

4

Entry Point RVA

RVA of entry point , needs to point to bytes 0xFF 0x25 followed by the RVA+0x4000000 in a section marked execute/read for EXEs or 0 for DLLs

20

4

Base Of Code

RVA of the code section, always 0x00400000 for exes and 0x10000000 for DLL.

24

4

Base Of Data

RVA of the data section.

 

This contains informative text only

The entry point RVA shall always be either the x86 entry point stub or be 0. On non-CLI aware platforms, this stub will call the entry point API of mscoree (_CorExeMain or _CorDllMain). The mscoree entry point will use the module handle to load the meta data from the image, and invoke the entry point specified in vthe CLI header.

End informative text

24.2.3.2       PE Header Windows NT-Specific Fields

These fields are Windows NT specific:

Offset

Size

Field

Description

28

4

Image Base

Always 0x400000 (see Section 23.1).

32

4

Section Alignment

Always 0x2000 (see Section 23.1).

36

4

File Alignment

Either 0x200 or 0x1000.

40

2

OS Major

Always 4 (see Section 23.1).

42

2

OS Minor

Always 0 (see Section 23.1).

44

2

User Major

Always 0 (see Section 23.1).

46

2

User Minor

Always 0 (see Section 23.1).

48

2

SubSys Major

Always 4 (see Section 23.1).

50

2

SubSys Minor

 Always 0 (see Section 23.1).

52

4

Reserved

Always 0 (see Section 23.1).

56

4

Image Size

Size, in bytes, of image, including all headers and padding; shall be a multiple of Section Alignment.

60

4

Header Size

Combined size of MS-DOS Header, PE Header, PE Optional Header and padding; shall be a multiple of the file alignment.

64

4

File Checksum

Always 0 (see Section 23.1).

68

2

SubSystem

Subsystem required to run this image.  Shall be either IMAGE_SUBSYSTEM_WINDOWS_CE_GUI (0x3) or IMAGE_SUBSYSTEM_WINDOWS_GUI (0x2).

70

2

DLL Flags

Always 0 (see Section 23.1).

72

4

Stack Reserve Size

Always 0x100000 (1Mb) (see Section 23.1).

76

4

Stack Commit Size

Always 0x1000 (4Kb) (see Section 23.1).

80

4

Heap Reserve Size

Always 0x100000 (1Mb) (see Section 23.1).

84

4

Heap Commit Size

Always 0x1000 (4Kb) (see Section 23.1).

88

4

Loader Flags

Always 0 (see Section 23.1)

92

4

Number of Data Directories

Always 0x10 (see Section 23.1).

 

 

24.2.3.3       PE Header Data Directories

The optional header data directories give the address and size of several tables that appear in the sections of the PE file. Each data directory entry contains the RVA and Size of the structure it describes.

Offset

Size

Field

Description

96

8

Export Table

Always 0 (see Section 23.1).

104

8

Import Table

RVA of Import Table, (see clause 24.3.1).

112

8

Resource Table

Always 0 (see Section 23.1).

120

8

Exception Table

Always 0 (see Section 23.1).

128

8

Certificate Table

Always 0 (see Section 23.1).

136

8

Base Relocation Table

Relocation Table, set to 0 if unused (see clause 24.3.1).

144

8

Debug

Always 0 (see Section 23.1).

152

8

Copyright

Always 0 (see Section 23.1).

160

8

Global Ptr

Always 0 (see Section 23.1).

168

8

TLS Table

Always 0 (see Section 23.1).

176

8

Load Config Table

Always 0 (see Section 23.1).

184

8

Bound Import

Always 0 (see Section 23.1).

192

8

IAT

RVA of Import Address Table, (see clause 24.3.1).

200

8

Delay Import Descriptor

Always 0 (see Section 23.1).

208

8

CLI Header

CLI Header with directories for runtime data, (see clause 24.3.1).

216

8

Reserved

Always 0 (see Section 23.1).

 

The tables pointed to by the directory entries are stored in on of the PE file’s sections; these sections themselves are described by section headers.

24.3      Section Headers

Immediately following the optional header is the Section Table, which contains a number of section headers. This positioning is required because the file header does not contain a direct pointer to the section table; the location of the section table is determined by calculating the location of the first byte after the headers.

Each section header has the following format, for a total of 40 bytes per entry:

Offset

Size

Field

Description

0

8

Name

An 8-byte, null-padded ASCII string. There is no terminating null if the string is exactly eight characters long.

8

4

VirtualSize

Total size of the section when loaded into memory in bytes rounded to Section Alignment. If this value is greater than Size of Raw Data, the section is zero-padded.

12

4

VirtualAddress

For executable images this is the address of the first byte of the section, when loaded into memory, relative to the image base.

16

4

SizeOfRawData

Size of the initialized data on disk in bytes, shall be a multiple of FileAlignment from the PE header. If this is less than VirtualSize the remainder of the section is zero filled. Because this field is rounded while the VirtualSize field is not it is possible for this to be greater than VirtualSize as well. When a section contains only uninitialized data, this field should be 0.

20

4

PointerToRawData

RVA to section’s first page within the PE file. This shall be a multiple of FileAlignment from the optional header. When a section contains only uninitialized data, this field should be 0.

24

4

PointerToRelocations

RVA of Relocation section.

28

4

PointerToLinenumbers

Always 0 (see Section 23.1).

32

2

NumberOfRelocations

Number of relocations, set to 0 if unused.

34

2

NumberOfLinenumbers

Always 0 (see Section 23.1).

36

4

Characteristics

Flags describing section’s characteristics, see below.

 

The following table defines the possible characteristics of the section.

Flag

Value

Description

IMAGE_SCN_CNT_CODE

0x00000020

Section contains executable code.

IMAGE_SCN_CNT_INITIALIZED_DATA

0x00000040

Section contains initialized data.

IMAGE_SCN_CNT_UNINITIALIZED_DATA

0x00000080

Section contains uninitialized data.

IMAGE_SCN_MEM_EXECUTE

0x20000000

Section can be executed as code.

IMAGE_SCN_MEM_READ

0x40000000

Section can be read.

IMAGE_SCN_MEM_WRITE

0x80000000

Section can be written to.

 

24.3.1      Import Table and Import Address Table (IAT)

The Import Table and the Import Address Table (IAT) are used to import the _CorExeMain (for a .exe) or _CorDllMain (for a .dll) entries of the runtime engine (mscoree.dll). The Import Table directory entry points to a one element zero terminated array of Import Directory entries (in a general PE file there is one entry for each imported DLL):

Offset

Size

Field

Description

0

4

ImportLookupTable

RVA of the Import Lookup Table

4

4

DateTimeStamp

Always 0 (see Section 23.1).

4

4

ForwarderChain

Always 0 (see Section 23.1).

12

4

Name

RVA of null terminated ASCII string “mscoree.dll”.

16

4

ImportAddressTable

RVA of Import Address Table (this is the same as the RVA of the IAT descriptor in the optional header).

20

20

 

End of Import Table. Shall be filled with zeros.

 

The Import Lookup Table and the Import Address Table (IAT) are both one element, zero terminated arrays of RVAs into the Hint/Name table. Bit 31 of the RVA shall be set to 0. In a general PE file there is one entry in this table for every imported symbol.

Offset

Size

Field

Description

1

4

Hint/Name Table RVA

A 31-bit RVA into the Hint/Name Table. Bit 31 shall be set to 0 indicating import by name.

2

2

 

End of table, shall be filled with zeros.

 

The IAT should be in an executable and writable section as the loader will replace the pointers into the Hint/Name table by the actual entry points of the imported symbols.

The Name/Hint table contains the name of the dll-entry that is imported.

Offset

Size

Field

Description

0

2

Hint

Shall be 0.

2

variable

Name

Case sensitive, null-terminated ASCII string containing name to import. Shall be “_CorExeMain” for a .exe file and “_CorDllMain” for a .dll file.

 

24.3.2      Relocations

In a pure CIL image, a single fixup of type IMAGE_REL_BASED_HIGHLOW (0x3) is required for the x86 startup stub which access the IAT to load the runtime engine on down level loaders.  When building a mixed CIL/native image or when the image contains embedded RVAs in user data, the relocation section contains relocations for these as well.   

The relocation section contains a Fix-Up Table. The fixup table is broken into blocks of fixups. Each block represents the fixups for a 4K page and block shall start on a 32-bit boundary. The last fixup block has PageRVA field set to 0.

Each fixup block starts with the following structure:

Offset

Size

Field

Description

0

4

PageRVA

The RVA of the block in which the fixup needs to be applied.

4

4

Block Size

Total number of bytes in the fixup block, including the Page RVA and Block Size fields, as well as the Type/Offset fields that follow.

 

The Block Size field is then followed by (BlockSize –8)/2 Type/Offset. Each entry is a word (2 bytes) and has the following structure:

Offset

Size

Field

Description

0

4 bits

Type

Stored in high 4 bits of word. Value indicating which type of fixup is to be applied (described below)

0

12 bits

Offset

Stored in remaining 12 bits of word. Offset from starting address specified in the Page RVA field for the block. This offset specifies where the fixup is to be applied.

 

To apply a fixup, a delta is calculated as the difference between the preferred base address, and the base where the image is actually loaded. The fixup applies the delta to the 32-bit field at Offset. If the image is loaded at its preferred base, the delta would be zero, and thus the fixups would not have to be applied.

 

24.3.3      CLI Header

The CLI header contains all of the runtime-specific data entries and other information.  The header should be placed in a read only, sharable section of the image.  This header is defined as follows:

Offset

Size

Field

Description

0

4

Cb

Size of the header in bytes

4

2

MajorRuntimeVersion

The minimum version of the runtime required to run this program, currently 2.

6

2

MinorRuntimeVersion

The minor portion of the version, currently 0.

8

8

MetaData

RVA of the physical meta data (see Chapter 23).

16

4

Flags

Flags describing this runtime image.  (see clause 24.3.3.1).

20

4

EntryPointToken

Token for the MethodDef or File of the entry point for the image

24

8

Resources

Location of CLI resources. (See Partition V_alink=Partition_V ).

32

8

StrongNameSignature

RVA of the hash data for this PE file used by the CLI loader for binding and versioning

40

8

CodeManagerTable

Always 0 (see Section 23.1).

48

8

VTableFixups

RVA of an array of locations in the file that contain an array of function pointers (e.g., vtable slots), see below.

56

8

ExportAddressTableJumps

Always 0 (see Section 23.1).

64

8

MangedNativeHeader

Always 0 (see Section 23.1).

 

 

24.3.3.1       Runtime Flags

The following flags describe this runtime image and are used by the loader.

Flag

Value

Description

COMIMAGE_FLAGS_ILONLY

0x00000001

Always 1 (see Section 23.1).

COMIMAGE_FLAGS_32BITREQUIRED

0x00000002

Image may only be loaded into a 32-bit process, for instance if there are 32-bit vtablefixups, or casts from native integers to int32. CLI implementations that have 64 bit native integers shall refuse loading binaries with this flag set.

COMIMAGE_FLAGS_STRONGNAMESIGNED

0x00000008

Image has a strong name signature.

COMIMAGE_FLAGS_TRACKDEBUGDATA

0x00010000

Always 0 (see Section 23.1).

 

24.3.3.2       Entry Point Meta Data Token

·              The entry point token (see Clause 14.4.1.2) is always a MethodDef token (see Section 21.24) or File token (see Section 21.19 ) when the entry point for a multi-module assembly is not in the manifest assembly.  The signature and implementation flags in metadata for the method indicate how the entry is run

24.3.3.3       Vtable Fixup

Certain languages, which choose not to follow the common type system runtime model, may have virtual functions which need to be represented in a v-table.  These v-tables are laid out by the compiler, not by the runtime.  Finding the correct v-table slot and calling indirectly through the value held in that slot is also done by the compiler. The VtableFixups field in the runtime header contains the location and size of an array of Vtable Fixups (see clause 14.5.1). V-tables shall be emitted into a read-write section of the PE file. 

Each entry in this array describes a contiguous array of v-table slots of the specified size.  Each slot starts out initialized to the metadata token value for the method they need to call.  At image load time, the runtime Loader will turn each entry into a pointer to machine code for the CPU and can be called directly.

Offset

Size

Field

Description

0

4

VirtualAddress

RVA of Vtable

4

2

Size

Number of entries in Vtable

6

2

Type

Type of the entries, as defined in table below

 

Constant

Value

Description

COR_VTABLE_32BIT

0x01

Vtable slots are 32 bits.

COR_VTABLE_64BIT

0x02

Vtable slots are 64 bits.

COR_VTABLE_FROM_UNMANAGED

0x04

Transition from unmanged to manged code.

COR_VTABLE_CALL_MOST_DERIVED

0x10

Call most derived method described by the token (only valid for virtual methods).

 

24.3.3.4       Strong Name Signature

This header entry points to the strong name hash for an image that can be used to deterministically identify a module from a referencing point (see Section 6.2.1.3).

24.4      Common Intermediate Language Physical Layout

This section contains the layout of the data structures used to describe a CIL method and its exceptions. Method bodies can be stored in any read-only section of a PE file. The MethodDef (see Section 21.24) records in metadata carry each method's RVA.

A method consists of a method header immediately followed by the method body, possible followed by extra method data sections (see Section 24.4.5), typically exception handling data.  If exception-handling data is present, then CorILMethod_MoreSects flag (see clause 24.4.4) shall be specified in the method header and for each chained item after that.

There are two flavors of method headers - tiny  (see clause 24.4.2) and fat (see clause 24.4.3). The three least significant bits in a method header indicate which type is present (see clause 24.4.1). The tiny header is 1 byte long and represents only the method's code size. A method is given a tiny header if it has no local variables, maxstack is 8 or less, the method has no exceptions, the method size is less than 64 bytes, and the method has no flags above 0x7. Fat headers carry full information - local vars signature token, maxstack, code size, flag. Method headers shall be 4-byte aligned.

24.4.1      Method Header Type Values

The three least significant bits of the first byte of the method header indicate what type of header is present.  These 3 bits will be one and only one of the following:

Value

Value

Description

CorILMethod_TinyFormat

0x2

The method header is tiny (see clause 24.4.2) .

CorILMethod_FatFormat

0x3

The method header is fat (see clause 24.4.3).

 

24.4.2      Tiny Format

Tiny headers use a 5 bit length encoding.  The following is true for all tiny headers:

·              No local variables are allowed

·              No exceptions

·              No extra data sections

·              The operand stack need be no bigger than 8 entries

The first encoding has the following format:

Start Bit

Count of Bits

Description

0

2

Flags (CorILMethod_TinyFormat shall be set, see clause 24.4.4).

2

6

Size of the method body immediately following this header.  Used only when the size of the method is less than 2^6 bytes.

 

24.4.3      Fat Format

The fat format is used whenever the tiny format is not sufficient.  This may be true for one or more of the following reasons:

·              The method is too large to encode the size

·              There are exceptions

·              There are extra data sections

·              There are local variables

·              The operand stack needs more than 8 entries

A fat header has the following structure

Offset

Size

Field

Description

0

12 (bits)

Flags

Flags (CorILMethod_Fat shall be set, see clause 24.4.4)

12 (bits)

4 (bits)

Size

Size of this header expressed as the count of 4-byte integers occupied

2

2

MaxStack

Maximum number of items on the operand stack

4

4

CodeSize

Size in bytes of the actual method body

8

4

LocalVarSigTok

Meta Data token for a signature describing the layout of the local variables for the method.  0 means there are no local variables present

 

24.4.4      Flags for Method Headers

The first byte of a method header may also contain the following flags, valid only for the Fat format, that indicate how the method is to be executed:

Flag

Value

Description

CorILMethod_Fat

0x3

Method header is fat.

CorILMethod_TinyFormat

0x2

Method header is tiny.

CorILMethod_MoreSects

0x8

More sections follow after this header (see Section 24.4.5).

CorILMethod_InitLocals

0x10

Call default constructor on all local variables.

 

24.4.5      Method Data Section

At the next 4-byte boundary following the method body can be extra method data sections. These method data sections start with a two byte header (1 byte flags, 1 byte for the length of the actual data)  or a four byte header  (1 byte for flags, and 3 bytes for length of the actual data). The first byte determines the kind of the header, and what data is in the actual section:

Flag

Value

Description

CorILMethod_Sect_EHTable

0x1

Exception handling data.

CorILMethod_Sect_OptILTable

0x2

Reserved, shall be 0.

CorILMethod_Sect_FatFormat

0x40

Data format is of the fat variety, meaning there is a 3 byte length.  If not set, the header is small with a  1 byte length

CorILMethod_Sect_MoreSects

0x80

Another data section occurs after this current section

 

Currently, the method data sections are only used for exception tables (see Chapter 18). The layout of a small  exception header structure as is a follows:

Offset

Size

Field

Description

0

1

Kind

Flags as described above.

1

1

DataSize

Size of the data for the block, including the header, say n*12+4.

2

2

Reserved

Padding, always 0.

4

n

Clauses

n small exception clauses (see Section 24.4.6).

 

The layout of a fat exception header structure is as follows:

Offset

Size

Field

Description

0

1

Kind

Which type of exception block is being used

1

3

DataSize

Size of the data for the block, including the header, say n*24+4.

4

n

Clauses

n fat exception clauses (see Section 24.4.6).

 

24.4.6      Exception Handling Clauses

Exception handling clauses also come in small and fat versions.

The small form of the exception clause should be used whenever the code size for the try block and handler code is smaller than or equal to 256 bytes.  The format for a small exception clause is as follows:

Offset

Size

Field

Description

0

2

Flags

Flags, see below.

2

2

TryOffset

Offset in bytes of try block from start of the header.

4

1

TryLength

Length in bytes of the try block

5

2

HandlerOffset

Location of the handler for this try block

7

1

HandlerLength

Size of the handler code in bytes

8

4

ClassToken

Meta data token for a type-based exception handler

8

4

FilterOffset

Offset in method body for filter-based exception handler

 

The layout of fat form of exception handling clauses is as follows:

Offset

Size

Field

Description

0

4

Flags

Flags, see below.

4

4

TryOffset

Offset in bytes of  try block from start of the header.

8

4

TryLength

Length in bytes of the try block

12

4

HandlerOffset

Location of the handler for this try block

16

4

HandlerLength

Size of the handler code in bytes

20

4

ClassToken

Meta data token for a type-based exception handler

20

4

FilterOffset

Offset in method body for filter-based exception handler

 

The following flag values are used for each exception-handling clause:

Flag

Value

Description

COR_ILEXCEPTION_CLAUSE_EXCEPTION

0x0000

A typed exception clause

COR_ILEXCEPTION_CLAUSE_FILTER

0x0001

An exception filter and handler clause

COR_ILEXCEPTION_CLAUSE_FINALLY

0x0002

A finally clause

COR_ILEXCEPTION_CLAUSE_FAULT

0x0004

Fault clause (finally that is called on exception only)