Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding 32-bit vs 64-bit in .Net

I've always skirted around the issue of building 64-bit desktop apps as I somehow got it in my head that it wasn't a trivial thing to do, plus I guess I didn't really understand the solution "configuration manager" dialog, platforms and configurations, either.

Anyway, I've just tried converting an existing application by simply changing all of the solution projects' platforms to x64, and it worked. The only issue I had was with a C++ DLL referenced by one of the projects, which needed rebuilding as x64.

So my question (finally) is: why did I only have an issue with the C++ DLL, but the many .Net assembly DLLs referenced by the projects in my solution were fine?

And what actually determines whether my application is built as 64-bit? I changed all of the solution's projects to "x64", but would it still have worked if they were left as "Any CPU", and I only changed the WPF (startup) project to "x64"?

Edit

So in the end it seems that I was simply able to set all project platforms to "Any CPU" rather than "x64" (the TFS server wasn't able to run unit tests with the latter). Even the projects referencing the unmanaged 64-bit DLL seem happy with this (not sure why).

The trick was to uncheck the Prefer 32-bit option in the WPF (startup) project's properties. The built application now runs as a 64-bit app, and TFS/unit tests run happily.

like image 863
Andrew Stephens Avatar asked Dec 14 '22 16:12

Andrew Stephens


1 Answers

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:

  1. Structures containing pointers will have their internal memory structure changes are they need more (or less) bytes to store pointers.
  2. 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.

like image 101
Matan Shahar Avatar answered Dec 16 '22 06:12

Matan Shahar