TL;DR: .Net assemblies are never assembled in to machine specific instructions, instead they are compiled into MSIL which runs under the .Net virtual machine, at runtime the VM uses the JITer to produce machine instructions which can be executed by the CPU.
The differences between 32bit
and 64bit
that "breaks" object file are either: Pointer size, Memory model and most importantly the instruction set architecture (ISA). Lets break down each point and see why .Net
assemblies are mostly unaffected.
Point Size (or Length)
In a 32bit
execution environment pointers are 32 bit long, and as you'd expect int 64 bit they are 64 bit long. This fact can break your assembly in one of two ways:
- Structures containing pointers will have their internal memory structure changes are they need more (or less) bytes to store pointers.
- Pointer arithmetic - some clever developers like to play around with pointers, masking them, adding them and so on. In some cases the size of the pointer may be integral for these computations to yield correct results.
Why .Net assemblies are not affected
Since .Net languages are safe
- you don't get to play with pointers (generally, unless you are under an unsafe context). Not having the option to play with pointers solves the 2nd point. As for the first point - this is the reason you can get the size (using sizeof
) of a class no the size of a struct containing class references.
Memory Model
The old 32bit
processors used to have a feature called Memory segmentation
which allowed the OS
or a Supervisor program
to declare regions of memory that are accessed using special CPU registers
called Segment registers
. In 64bit
this feature is mostly disabled. So programs compile to work under memory segmentation may have problems working in a non segmented environment.
Why .Net assemblies are not affected
Generally we don't deal with memory at this low level, this is possible because of the .Net virtual machine
as explained in the next point.
Instruction Set Architecture
32bit
and 64bit
(x86
and AMD64
) are similar but completely different ISAs
which means that code which was assembled to run under one will not run under the other. So unlike the other two points this one will break your assembly regardless of what you've written in it.
Why .Net assemblies are not affected
So how could .Net assemblies possibly be compiled as Any CPU
? The trick is that when you compile a .Net language you never assemble it. That means when you press compile
you only compile your code into an intermediate object (known as a .Net assembly) that is not assembled (ie. not ran through an assembler).
Usually when you compile code in C/++
you first run it through a compile the generates some assembly instructions
these instructions are then passed down to an assembler
which generates machine instructions
. The CPU is only capable of executing these machine instructions
.
.Net languages are different, as mentioned above, when you compile a .Net language you run it through a compiler and stop there, no assembler involved. The compile here, too, generates some assembler instructions
but unlike the instructions produced by a c/c++
compile these instructions are machine agnostic, they are written in language called MSIL - Microsoft intermediate language
. No CPU knowns how to execute these instructions, since just like ordinary assembler instructions they too must be assembled into machine instructions.
The trick is that this all happens at run time, when a user opens you program he launches an instance of the .Net runtime
in which your program runs, your program never runs directly against the native machine itself. When your program needs to call a method a special component in the .Net virtual machine called the JITer - just in time compiler
is tasked with assembling this method from MSIL to machine instructions specific for the machine the .Net framework was installed on.