Moving from 32-bit to 64-bit application development on .NET Framework
This blog aims at clarifying the issues which 32-bit developers might be having when they start migrating to the 64-bit platform. My observations below would be concentrated towards the .NET Framework related technologies. Most of the things I discuss below are available at various scattered places. I am just aiming at collecting them at a single place to help developers migrating to the 64-bit platform.
Size of the Redist
As compared to the pure 32-bit platform offerings, the 64-bit offerings are usually double the size. This means the time to download and setup the 64-bit .NET Redistributable or anything similar also increases. But the reason for the same is the fact that the 64-bit offerings come clubbed with the 32-bit offerings. It enables applications to run in the native 64-bit mode as well as the WoW64 mode (which is the 32-bit version).
Unlike the 32-bit .NET Framework, when a user installs a 64-bit Redist on the machine there are two Framework directories that get created. They are
Notice the difference in the two directories. The Framework directory contains the assemblies that are specific to the WoW64 (32-bit emulation on 64-bit platforms) world. The Framework64 directory on the other hand contains assemblies that are specific to the 64-bit platform. And assemblies that are platform agnostic are available in either or both the directories as per need basis.
Creating platform specific apps
Most of the language compilers (like C#) now offer a /platform switch. By using this switch, developers can create binaries targeted for a specific platform type or binaries that are platform agnostic. There are four types of binaries that are emitted
· anycpu – platform agnostic
· x86 – 32-bit platform specific
· x64 – x64 platform specific
· itanium – IA platform specific
(Read more about x64 and IA – their differences, similarities and FAQs here)
By default the compilers (like C#) emit anycpu binaries (also called portable assemblies) which are platform agnostic. In case the users want to create binaries specific to a platform, they can use the appropriate switch and be done.
The above concept of /platform switch enables cross compilation of binaries. Cross compilation means compiling binaries to a specific platform type (different from the current platform). This is mostly used in terms of compiling and creating 64-bit assemblies from a 32-bit compiler and vice versa. One point to note here is that cross compilation usually refers to compiling to different target types from the compilers shipped with the 64-bit Redist (WoW and 64-bit). The reason for this is that, while trying to compile for a 64-bit platform from a pure 32-bit machine, the 32-bit Redist would not have the components that are 64-bit specific. In such cases the compilation might go through with warning but execution might lead to runtime exceptions. Also, one should note that while cross compiling, users should stick to the platform architecture type of the machine, viz. x64 or IA. Assemblies for IA and x64 are specific to the platform architecture and cross compilation across architectures is not advised.
GAC in 64-bit platforms
Now, that we know that there are multiple types of assemblies and ways to create specific assembly types, the question comes about the Global Assembly Cache (or GAC) in the 64-bit machines. Unlike the .NET Framework 1.1, where the 32-bit specific offerings used to have a single GAC present the model has changed for .NET Framework 2.0 (Whidbey). There are 3 types of GAC’s now available
While the 32-bit platforms have GAC_32 and GAC_MSIL, the 64-bit have all the three. GAC_32 contains assemblies that are specific to the 32-bit platform and GAC_64 specific to the 64-bit platform. The GAC_MSIL contains the assemblies that are platform agnostic. While trying to load assemblies from the GAC, the CLR first looks at the GAC specific to the platform. If it is unable to find the same, it then moves to the GAC_MSIL.
Apart from these, we also have the conventional GAC present in all platforms which homes the .NET 1.0/1.1 assemblies which are platform independent.
Corflags.exe is a tool that is shipped as a part of the .NET Framework SDK. It is a powerful tool which allows users to view and configure the corflags section of a PE image. All the data related to platforms is available in this part of the PE image. Let us view the options available from this tool.
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
Usage: Corflags.exe Assembly [options]
If no options are specified, the flags for the given image are displayed.
/ILONLY+ /ILONLY- Sets/clears the ILONLY flag
/32BIT+ /32BIT- Sets/clears the 32BIT flag
/UpgradeCLRHeader Upgrade the CLR Header to version 2.5
/RevertCLRHeader Revert the CLR Header to version 2.0
/Force Force an assembly update even if the image is
strong name signed.
WARNING: Updating a strong name signed assembly
will require the assembly to be resigned before
it will execute properly.
/nologo Prevents corflags from displaying logo
/? or /help Display this usage message
A look at an example of a PE header seen from this tool for a 32-bit assembly is below
Version : v2.0.50727
CLR Header : 2.5
PE : PE32
CorFlags : 11
ILONLY : 1
32BIT : 1
Signed : 1
Here is what each component of the header means
Version: Contains the version of .NET Redist with which the binary is built.
CLR Header: 2.0 indicates a .Net 1.0 or .Net 1.1 (Everett) image while 2.5 indicates a .Net 2.0 (Whidbey) image.
CorFlags: This is computed by OR’g specific flags to indicate whether the image is ILONLY, its bitness etc. and is used by the loader.
ILONLY: Managed images are allowed to contain native code. To be “anycpu” an image shall only contain IL.
32BIT: Even if you have an image that only contains IL it still might have platform dependencies, the 32BIT flag is used to distinguish “x86” images from “anycpu” images. 64-bit images are distinguished by the fact that they have a PE type of PE32+.
The most interesting aspect is the PE and the 32BIT flag of the header. These combine to specify the assembly types. Here is how they would look like for:
· anycpu: PE = PE32 and 32BIT = 0
· x86: PE = PE32 and 32BIT = 1
· 64-bit: PE = PE32+ and 32BIT = 0
Using corflags tool, users can forcefully change the PE headers of any assembly to make them behave the way they want to. Users can force most of the flags to the values as wanted. However, it is advised to use compiler switches instead of this tool to create binaries with specific characteristics as desired.
64-bit Design Considerations
Apart from what all I have discussed above, there are surely some design considerations that users need to take care of while creating applications for 64-bit/agnostic platforms. These are discussed pretty well in the whitepaper here. So I would not go and re-invent the wheel.
Other helpful resources
Other related blogs