Managed code assemblies can be a strange, many-headed beast. I say it has many heads because there are lots of nuances that have a significant impact on how and where managed code assemblies can be executed.

There are two main categories of managed code assemblies:

  • Pure MSIL
  • Mixed-mode

Pure MSIL assemblies contain only platform-independent byte code. At runtime this byte code has to be compiled (a.k.a. jitted) to the processor's native machine code. Pure MSIL code can run on any .NET platform be it i386, amd64 or ia64. It can even be run on Mono platforms without modification.

Mixed-mode assemblies contain a mix of native machine code and platform-independent byte code. Because it contains native machine code a mixed-mode assembly can only run on the platform for which it was compiled.

That didn't sound so bad, did it? Pure MSIL assemblies and mixed-mode assemblies, it's simple. But wait!

A pure MSIL assembly can be made into a platform-specific assembly. It'll still contain nothing but platform-independent byte code. At runtime this byte code will still need to be compiled (a.k.a. jitted) to the processor's native machine code. But it can only run on that one specific platform. See Spanky J's blog on generating platform-specific assemblies for more info on how all these different types of assemblies interact with one another.

Right about now you should be wondering how to tell the difference between a truly platform independent pure MSIL assembly, an i386 platform-specific pure MSIL assembly and an amd64 platform specific pure MSIL assembly. It turns out that getting the answer is pretty easy, but you'll have to take two properties into account: the assembly's container, EXE or DLL, file format and the assembly's .corflags metadata value. To see this information you'll need to run corflags.exe. (This is a free tool and it's included with the Windows SDK.) See Spanky J's blog on flipping bits on managed assemblies for more info on how to run corflags.exe.

Coreflags.exe dumps out the assembly's container file format info in the "PE" field. It dump's the assembly's .corflags metadata value in 4 different fields, but we'll only need to look at two of them, the "ILONLY" and 32Bit fields.

Let's go with the easy one first. This is a pure MSIL assembly that can be run on any platform.

Table 1: MSIL assembly that can be loaded in any environment

Version : v2.0.50727

CLR Header: 2.5

PE : PE32

CorFlags : 1

ILONLY : 1

32BIT : 0

Signed : 1

You should note that the PE field's value is PE32, the ILONLY field's value is 1 and the 32Bit field's value is 0. The PE field's PE32 value means the assembly can be loaded on any platform that knows how to read the PE32 file format. All .NET platforms and all Mono platforms know how to read the PE32 file format. The ILONLY field's 1 value means that the assembly contains only platform-independent byte code. The 32Bit field's 0 value means that the assembly will be jitted to platform native code at runtime. All this means that it will run as a native 32-bit app on 32-bit systems and a native 64-bit app on 64-bit systems.

Now we'll take it up a notch and look at a pure MSIL assembly that can be run only on an i386 platform.

Table 2: MSIL assembly that can be loaded only in an i386 environment

Version : v2.0.50727

CLR Header: 2.5

PE : PE32

CorFlags : 3

ILONLY : 1

32BIT : 1

Signed : 1

You should note that the PE field's value is PE32, the ILONLY field's value is 1 and the 32Bit field's value is 1. I've already explained the significance of the values for the PE and ILONLY fields. The 32Bit field's 1 value means that the assembly will be jitted to i386 platform specific code at runtime. All this means that it will run as a native 32-bit app on 32-bit systems and run as a native 32-bit app within the WoW64 subsystem 64-bit systems. (Remember that 64-bit Windows has the WoW64 subsystem that allows it to load and run code for the i386 platform.)

Next we'll look at a pure MSIL assembly that can be run only on an amd64 platform.

Table 3: MSIL assembly that can be loaded only in an amd64 environment

Version : v2.0.50727

CLR Header: 2.5

PE : PE32+

CorFlags : 1

ILONLY : 1

32BIT : 0

Signed : 1

You should note that the PE field's value has changed to PE32+, the ILONLY field's value remains as 1. and the 32Bit field's value is back to 0. The PE field's PE32+ value (it's the "+" that makes the difference) means the assembly can be loaded on any platform that knows how to read the PE32+ file format and the only platform that knows how to do that is the 64-bit .NET platform. (Currently Mono doesn't know how to read the PE32+ file format.) All this means that it will run as a native 64-bit app on 64-bit systems and only on 64-bit systems.

Let's not forget about mixed-mode assemblies.

Table 4: Mixed-mode assembly that can be loaded only in an i386 environment

Version : v2.0.50727

CLR Header: 2.5

PE : PE32

CorFlags : 16

ILONLY : 0

32BIT : 0

Signed : 1

 

Table 5: Mixed-mode assembly that can be loaded only in amd64 environment

Version : v2.0.50727

CLR Header: 2.5

PE : PE32+

CorFlags : 16

ILONLY : 0

32BIT : 0

Signed : 1

A mixed-mode assembly will always have the ILONLY field's value as 0. The significant field is the PE field.

That's just about all there is to say on this topic. However, if you've been paying attention you may have noticed that one combination of these fields hasn't been covered. What would it mean if the assembly had the following field values?

Table 6: A fictitious assembly that cannot be loaded at all

Version : v2.0.50727

CLR Header: 2.5

PE : PE32+

CorFlags : 16

ILONLY : 0

32BIT : 1

Signed : 1

An assembly with these values wouldn't be valid. It just wouldn't load at all. The compiler won't allow you to create an assembly with these values.

 

UPDATE: 5/12/2011...

If you are using Mono the rough equivalent of corflags.exe is the pedump tool. The pedump tool dumps out much more info than corflags.exe. The section you'll be interested in the CLI header, particularly the Flags property. 

          CLI header size: 72

         Runtime required: 2.5

                    Flags: ilonly, 32bits, no-trackdebug, notsigned

                 Metadata: 0x00003bb0 [0x00002f1c]

        Entry Point Token: 0x06000039

             Resources at: 0x00003bac [0x00000004]

           Strong Name at: 0x00000000 [0x00000000]

          Code Manager at: 0x00000000 [0x00000000]

          VTableFixups at: 0x00000000 [0x00000000]

             EAT jumps at: 0x00000000 [0x00000000]

If the assembly contains only pure MSIL the "ilonly" flag will be present. For mixed-mode assemblies the "contains native" flag will be present instead. The flag "32bits" means that it will run as a native 32-bit app on 32-bit systems and run as a native 32-bit app within the WoW64 subsystem 64-bit systems. Platform independent assemblies will have the "32/64" flag instead.