A .il file has no header, no signature and no required first line: it is a list of declarations, each starting with a directive (a word that begins with a dot) or, for the preprocessor, a #. ilasm reads them in order. The order mostly does not matter, with two exceptions: a .typedef name (and a #define name) only means something after its definition, because the lexer looks names up as it reads them, and the declarations of one type sit between its braces. Several files can make up one assembly through #include (see the preprocessor).
The top-level declarations
The grammar of ilasm (decl in asmparse.y) allows these declarations outside a type or method. The standard (ECMA-335 II.5.10) lists most of the same ones; the declarations it does not list are marked ilasm.
| Declaration | What it does | Page |
|---|---|---|
.assembly Name { … } | names the assembly: version, public key, hash algorithm, locale, custom attributes, security declarations | Assemblies and modules |
.assembly extern Name { … } | references another assembly (version, public key token, hash); optionally as an alias | Assemblies and modules |
.module [Name] | names the module; .module extern references another module of the assembly | Assemblies and modules |
.file [nometadata] Name [.hash = (…)] [.entrypoint] | lists another file of the assembly | Assemblies and modules |
.class header { … } | defines a class, value type, interface or enum | Classes |
.class extern Name { … } | declares an exported type or a type forwarder; the older spelling is .export | Assemblies and modules |
.namespace Name { … } ilasm | a namespace that prefixes the names of the types inside the braces; it can nest | Classes |
.method header { … } | a global method (a method of the module itself, outside any type) | Methods |
.field declaration | a global field | Fields and data |
.data declaration | bytes in the data section of the file | Fields and data |
.mresource [public | private] Name { … } | a manifest resource (.manifestres is an alias of it in the keyword table) | Assemblies and modules |
.custom declaration | a custom attribute on the current item, or with an owner in brackets on another item | Custom attributes |
.permission, .permissionset | a declarative security declaration | Custom attributes |
.subsystem n, .corflags n, .file alignment n, .imagebase n, .stackreserve n | fields of the PE headers and the CLR header (see below) | this page |
.vtfixup [n] … at label | a table of method entry points for unmanaged callers; .vtable = (…) is the deprecated form | Marshalling and platform invoke |
.line or #line, .language ilasm | source line information and the source language, for debuggers | Methods |
.typedef, .typelist, .mscorlib ilasm | aliases for types, members and custom attributes; a list of classes; marks the assembly as the system assembly | Preprocessor and conveniences |
#define, #undef, #ifdef, #ifndef, #else, #endif, #include, and a lone ; | compilation control, handled while reading the tokens | Preprocessor and conveniences |
The header directives
Five directives set fields of the PE and CLR headers of the file that ilasm writes. They take integer constants (hexadecimal is usual). Each also has a command-line option of ilasm.
| Directive | Field | Rules in the grammar |
|---|---|---|
.subsystem n | the Subsystem value of the NT optional header: 2 for a Windows GUI program, 3 for a console program | none checked by the parser (the option is /SUBSYSTEM) |
.corflags n | the Flags of the CLR header: 0x00000001 is ILONLY (ildasm prints ILONLY as a comment after the value) | none (/FLAGS) |
.file alignment n | FileAlignment of the NT optional header | a power of two from 0x200 to 0x10000, else an error (/ALIGNMENT) |
.imagebase n | ImageBase of the NT optional header (a 64-bit constant) | a multiple of 0x10000, else an error (/BASE) |
.stackreserve n | SizeOfStackReserve of the NT optional header | none (/STACK) |
The numbers of .subsystem and the others are those of the Windows PE format; see the PE file pages. The standard lists .subsystem and .corflags (II.6.2) but not the other three.
What ildasm writes, and in what order
ildasm writes a file that ilasm can read back. The order of its output, read from the source of ildasm (DumpFile in dasm.cpp) and from its usual output, is:
- comment lines about the tool and, with the options for them, the CLR header details, statistics and metadata dump (all inside
//comments or a block comment); .mscorlibwhen the assembly is the system library, then the.typelist, then the manifest (DumpManifestindman.cpp): the comment// Metadata version: …, the.module externlines, the.assembly externdeclarations, the.assembly(with.hash algorithmbefore.ver), the.file,.class externand.mresourcedeclarations, then.modulewith a comment// MVID: {…}, and then the header directives (.imagebase,.file alignment,.stackreserve,.subsystem,.corflags), the last two followed by a comment that names the value (// WINDOWS_CUI,// ILONLY);- the
.typedefaliases ildasm used for long names, if any; - the global fields and global methods, each group under a banner comment (
// ================== GLOBAL METHODS =========================); - the classes under the banner
// =============== CLASS MEMBERS DECLARATION ===================, nested classes inside their enclosing class; with the/FORWARDoption a first pass underCLASS STRUCTURE DECLARATIONlists only the class headers; - custom attributes that have no owner printed above, and the
.datablobs of the data section, namedD_,T_(thread-local) orI_(in the code section) followed by the address of the data.
Inside a method ildasm indents with two spaces, writes a label IL_ and four hexadecimal digits of the byte offset before each instruction (IL_0001: ldstr "x": the label, a colon, two spaces, the opcode name padded to ten characters), writes .maxstack 8 with two spaces, and closes with a comment such as // end of method Program::Main. A comment like // Code size 13 (0xd) gives the length of the IL in bytes. Names that are not plain identifiers are printed in single quotes, and a quote inside is written as an octal escape.
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89)
.ver 4:0:0:0
}
.assembly Hello
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module Hello.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "Hello"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Program::Main
} // end of class Program
void and after .maxstack are how ildasm prints them.Encoding and conventions
- Encoding. ilasm reads UTF-8, with or without the three-byte byte order mark, and UTF-16 little-endian when the file starts with
FF FE(the lexer says “Source file is UNICODE”); a UTF-32 little-endian file (FF FE 00 00) is refused with “UTF-32 LE is not supported”. Only the ASCII letters, digits and a few signs make bare names; any other character belongs in a quoted name or a string. - Line ends are only blanks to the lexer, apart from ending a
//comment and ending a string that has no closing quote. A\before a line end continues a string. - Case. Everything is case-sensitive:
Publicis not the keywordpublicandLdarg.0is not an instruction. - Hand-written files follow the examples of the standard: braces on their own lines, two-space indentation, short opcodes and explicit
.maxstack. Generated files (ildasm, the compilers’ IL listings, SharpLab) keepIL_xxxx:labels, because branches and exception ranges are addressed by them. - Default extension is
.il;ilasmpicks it when the file name has none (see Tooling).
Not described
Known, not yet covered
- Reference: the order of ildasm’s output was taken from
DumpFileindasm.cppandDumpManifestindman.cpp; the order of the lines inside each declaration was read only for a reference (.publickeytokenbefore.ver). The ildasm options that change the layout (/TOKENS,/BYTES,/SOURCE,/LINENUM) are listed on the tooling page, not shown in examples.
Unknown
- Reference: the example listing was written, not captured: no .NET SDK or ildasm is installed on this Mac.