Thursday Night

Paul Betts’s personal website / blog / what-have-you

How 64-bit Windows works – cause no one seems to get it

comments

64-bit is the future

Despite everyone dragging their heels as much as possible, eventually developers and users will start moving soon to 64-bit; in WinSE, we predict that this is going to accelerate quite a bit because computer manufacturers are starting to sell machines with more than 4GB of RAM, and customers start complaining when they pay for 4GB and only get 3ish or so

However, even among experienced developers, I hear a lot of confusion about how 64-bit works, and how I can run 32-bit programs on 64-bit. Listening to the .NET Rocks episode on the subject shows that even fairly knowledgeable guys can be pretty confused about how all of this works. Hopefully, this blog post can set the record straight for some of you – if anything seems like it makes no sense, leave a comment or send me an Email and I’ll try to update the entry!

The one thing to remember

Whether you’re on Windows, Linux, or OS X, there’s one fundamental problem that underlies all of the complexity; if you remember this one fact, everything else in this article will make sense. Here it is:

You can not load a 32-bit library in a 64-bit process.

Repeat that out loud a few times, it’ll help. This is a fundamental aspect of 64-bit computing and 32-bit compatibility mode on the amd64 and IA64 architecture, it’s not specific to Windows. This rule shows up in a lot of places that you wouldn’t expect, like Explorer shell extensions, browser plugins like Flash, and multimedia codecs in Linux.

In an “all 64-bit” world, there are no problems – everything is simple and works pretty much just like 32-bit. However, one of the advantages of the amd64 architecture is that it allows 32-bit code to be run in compatibility mode, with almost no loss in performance. Being able to run both 64-bit and 32-bit apps in the same OS is huge for application compatibility, and AMD was very smart to make it possible (unlike Itanium, which pretty much threw x86 compatibility out the window, and felt the much-deserved consequences). However, to make this actually work is most-definitely not a trivial affair – there’s a lot of things to fix up to make everything as seamless as 32-bit.

How does Windows deal with it?

The core infrastructure in Windows NT to handle this is called WOW64 – this code manages emulating the 32-bit architecture (either by only setting the processor mode in amd64, or actually performing emulation in IA64). As a result of the rule I’ve described above, there are actually two copies of almost every API in Windows – one for native binaries and one for 32-bit binaries.

For every system DLL, there’s two versions!

When a 32-bit app gets launched on x64, all of the DLLs being used are from the \Windows\SysWOW64 directory; usually these follow an identical codepath to the 32-bit version of the DLL, though certain DLLs like user32.dll actually operate differently in WOW64 mode. There’s a lot of stuff behind the scenes to make this happen, such as Filesystem Redirection (almost all apps don’t need to know anything about WOW64!) However, there’s still one problem – for 100% user-mode DLLs, this solution works, but the kernel is 64-bit, and only 64-bit. We surely can’t load a separate 32-bit kernel running at the same time!

When it comes down to it, you need to get the data to the kernel in a different way, some sort of IPC mechanism – in Windows this involves the RPC/LPC mechanism, or more commonly, via a syscall. When a 32-bit app calls CreateFileW, it calls NtCreateFile, which pushes the appropriate parameters into registers, and invokes the SYSENTER instruction to switch back into 64-bit mode. This mechanism works because there is only the kernel to worry about transitioning to, and it can have special knowledge as to whether the calling process was 32-bit or 64-bit.

What about .NET?

.NET is an interesting case; when you install the 64-bit .NET framework, two versions of the CLR are installed as well as the GAC (knowing that there are two GAC’s can avoid a major Gotcha!). However, most .NET binaries are MSIL – since the binary is in a processor-agnostic instruction set, which one does it choose?

The answer is, “usually 64-bit, except for when it doesn’t.” .NET assemblies can be marked as “only run on x86/x64″ via the compiler or via the Corflags tool,and there’s one very important reason to do so, if you use P/Invoke to a library that only has a x86 version. Unfortunately, the check for x86-only will only extend so far down the dependency chain, so if you have a library who uses a library who uses a native library, the CLR will end up throwing a BadImageFormatException.

Debugging using WinDbg

There’s one special trick to debugging an x86 binary on 64-bit with WinDbg, via this blog entry, that if you’re having trouble getting a call stack, you need to load the wow64exts debugger extension and use the “!sw” command to switch between 64-bit and 32-bit mode. VS is luckily smart enough to handle this for you (though I haven’t tried it recently!)

Written by Paul Betts

October 14th, 2008 at 12:13 am

Posted in Microsoft, Programming

Leave a Reply