Levels/Applications, the loader & the Win32 environment/WOW64 and cross-architecture compatibility

Level 0 · Chapter 4

WOW64 and cross-architecture compatibility

How 32-bit user-mode applications keep running on a 64-bit kernel, two decades after 64-bit Windows shipped.

The DLL loader assumed a process is either fully 32-bit or fully 64-bit, loading only DLLs that match. WOW64 — "Windows on Windows 64" — is the layer that makes that assumption safe to make, even when a 32-bit program runs on a machine whose kernel, drivers, and most other processes are all native 64-bit code.

Why this had to exist

When 64-bit Windows shipped, the practical reality was that almost no software existed for it yet. Microsoft's answer wasn't "recompile everything" — it was "make the existing 32-bit binaries work, unmodified, as a first-class experience." Two decades later, WOW64 is still there, still actively maintained, and still the reason a 32-bit installer from 2009 can run today on a machine whose kernel has never been 32-bit.

The core design decision: the kernel itself stays entirely native 64-bit. There is no 32-bit kernel mode on 64-bit Windows. All the translation work happens in user mode, which keeps the kernel simple and means a 32-bit process can't accidentally rely on any 32-bit kernel behavior that doesn't exist.

What actually runs where

A 32-bit process on 64-bit Windows has:

  • A 32-bit image (the EXE), loaded and relocated exactly as described in the DLL loader chapter — but only ever linked against 32-bit DLLs, from a parallel System32-equivalent directory (confusingly named SysWOW64 — the "native" 64-bit System32 is off-limits to it).
  • A WOW64 support layer — wow64.dll, wow64win.dll, wow64cpu.dll — loaded alongside the 32-bit runtime, invisible to the 32-bit code itself.
  • Address space limited to the low 2 GB (or up to 4 GB, if the image opts in) by default, since 32-bit pointers can only address that much regardless of what the underlying 64-bit hardware could support.

The actual translation: what happens at a syscall

The interesting mechanics happen exactly at the point Ntdll & the user/kernel boundary describes: the transition from user mode into the kernel. A 32-bit program still calls what looks like an ordinary 32-bit Nt* function in its own 32-bit ntdll.dll. But instead of executing a 32-bit syscall/sysenter instruction directly, that call is redirected into wow64cpu.dll, which:

  1. Marshals the arguments. 32-bit calling conventions and structure layouts don't match 64-bit ones — pointers are half the size, some structures have different alignment. Every argument has to be widened and repacked into the layout the native 64-bit kernel expects.
  2. Switches processor mode. The thread transitions from 32-bit (compatibility) mode to full 64-bit mode for the duration of the kernel call.
  3. Issues the real, native 64-bit system call, using the exact same kernel entry point a native 64-bit process would use — the kernel has no idea it's servicing a translated call; from its point of view this is an ordinary 64-bit request.
  4. Marshals the results back into 32-bit-shaped output on return, and switches the processor back to 32-bit mode before returning control to the caller.

This is why WOW64 is accurately described as a thunk layer, not an emulator in the traditional sense: it doesn't reinterpret 32-bit machine instructions one by one (that would be far too slow for routine syscalls) — it lets 32-bit user-mode code run directly on the CPU at full speed, and only intervenes at the narrow, well-defined boundary where a mode transition into the kernel would otherwise happen.

Keeping two worlds from colliding

Because 32-bit and 64-bit code can't share pointers or structure layouts, Windows goes out of its way to keep them from mixing by accident:

  • File system redirection: a 32-bit process reading from %windir%\System32 is transparently redirected to %windir%\SysWOW64, so it never sees native 64-bit binaries it couldn't load anyway. (Sysnative is the explicit escape hatch for a 32-bit process that deliberately needs to reach the real System32.)
  • Registry redirection: similarly, HKLM\Software reads and writes for a 32-bit process are redirected to HKLM\Software\WOW6432Node, so 32-bit and 64-bit installations of the same product don't stomp on each other's settings.
  • Separate module lists: the loader mechanisms from the previous chapter run entirely within one architecture's world at a time — a 32-bit process's PEB_LDR_DATA only ever lists 32-bit modules.

A common mistake

It's tempting to imagine WOW64 as "a whole emulated 32-bit operating system living inside the real one." It isn't — there's exactly one kernel, and it's always native. WOW64 is a translation and redirection layer that lets 32-bit user-mode code coexist with a 64-bit kernel and 64-bit siblings, not an independent OS-within-an-OS. Everything privileged — scheduling, memory management, security checks — is still handled by the same 64-bit Executive every other process on the machine uses.

Where this connects

  • The mode switch this chapter describes happens at exactly the boundary Ntdll & the user/kernel boundary covers for native processes — WOW64 adds one more hop before that same boundary, not a different one.
  • ARM64 Windows uses a conceptually similar mechanism (and a genuine instruction-level emulator, for x86 code specifically) to run both 32-bit and 64-bit x86 applications on ARM hardware — the marshaling problem is the same, even though the underlying translation technique differs.