X86/X64 on Windows ARM build

** This are some notes i have taken/prepared few years back while doing some research on x86/64 bit support on windows arm build. I couldnot finish it and then never got time to work on it again, this may be useful for someone.**

windows 10 supported x86 on arm
windows 11 added support for x64 as well

from: https://learn.microsoft.com/en-us/windows/win32/winprog64/wow64-implementation-details

WOW64 is an emulator which allows 32 bit windows apps as well as 32 bit arm application in 64bit arm windows.
wow64 emulator runs in user mode and it provides interfaces between 32 bit version of ntdll.dll and the kernel of proceccsor and intercepts kernel calls.
following are the dlls used for this:
Wow64.dll provides the core emulation infrastructure and the thunks for the Ntoskrnl.exe entry-point functions.
Wow64Win.dll provides thunks for the Win32k.sys entry-point functions.
(x64 only) Wow64Cpu.dll provides support for running x86 programs on x64.
(Intel Itanium only) IA32Exec.bin contains the x86 software emulator.
(Intel Itanium only) Wowia32x.dll provides the interface between IA32Exec.bin and WOW64.
(ARM64 only) xtajit.dll contains the x86 software emulator.
(ARM64 only) wowarmw.dll provides support for running ARM32 programs on ARM64.

These DLLs, along with the 64-bit version of Ntdll.dll, are the only 64-bit binaries that can be loaded into a 32-bit process. On Windows 10 on ARM, CHPE (Compiled Hybrid Portable Executable) binaries may also be loaded into an x86 32-bit process.

At startup, Wow64.dll loads the x86 version of Ntdll.dll (or the CHPE version, if enabled) and runs its initialization code, which loads all necessary 32-bit DLLs.
Almost all 32-bit DLLs are unmodified copies of 32-bit Windows binaries, though some are loaded as CHPE for performance reasons. Some of these DLLs are written to behave differently on WOW64 than they do on 32-bit Windows, usually because they share memory with 64-bit system components. All user-mode address space above the 32-bit limit is reserved by the system.

Instead of using the x86 system-service call sequence, 32-bit binaries that make system calls are rebuilt to use a custom calling sequence. This calling sequence is inexpensive for WOW64 to intercept because it remains entirely in user mode. When the custom calling sequence is detected, the WOW64 CPU transitions back to native 64-bit mode and calls into Wow64.dll. Thunking is done in user mode to reduce the impact on the 64-bit kernel and to reduce the risk of a bug in the thunk that might cause a kernel-mode crash, data corruption, or a security hole. The thunks extract arguments from the 32-bit stack, extend them to 64 bits, then make the native system call.

Registry Redirector – isolates 32-bit and 64-bit applications by providing separate logical views of certain portions of the registry on WOW64. The registry redirector intercepts 32-bit and 64-bit registry calls to their respective logical registry views and maps them to the corresponding physical registry location. The redirection process is transparent to the application. Therefore, a 32-bit application can access registry data as if it were running on 32-bit Windows even if the data is stored in a different location on 64-bit Windows.

Redirected keys are mapped to physical locations under Wow6432Node. For example, HKEY_LOCAL_MACHINE\Software is redirected to HKEY_LOCAL_MACHINE\Software\Wow6432Node. However, the physical location of redirected keys should be considered reserved by the system. Applications should not access a key’s physical location directly, because this location may change. For more information, see Accessing an Alternate Registry View.

Windows 10 on ARM: Redirected 32-bit ARM keys are mapped to physical locations under WowAA32Node.

To help 32-bit applications that write REG_SZ or REG_EXPAND_SZ data containing %ProgramFiles% or %commonprogramfiles% to the registry, WOW64 intercepts these write operations and replaces them with “%ProgramFiles(x86)%” and “%commonprogramfiles(x86)%”. For example, if the Program Files directory is on the C drive, then “%ProgramFiles(x86)%” expands to “C:\Program Files (x86)”. The replacement occurs only if the following conditions are met:

The string must begin with %ProgramFiles% or %commonprogramfiles%. If the string begins with a space or any character other than %, it is not replaced.
The case of %ProgramFiles% or %commonprogramfiles% must be exactly as shown because the string comparison is case-sensitive. For example, if the string begins with %CommonProgramFiles% instead of %commonprogramfiles%, it is not replaced.
The string cannot exceed MAX_PATH*2+15 characters. If it exceeds this length, it is not replaced.
The key cannot be opened with KEY_WOW64_64KEY. This flag specifies that operations on the key should be performed on the 64-bit registry view, so it is not replaced. For more information, see Accessing an Alternate Registry View.

File System Redirector – The %windir%\System32 directory is reserved for 64-bit applications on 64-bit Windows. Most DLL file names were not changed when 64-bit versions of the DLLs were created, so 32-bit versions of the DLLs are stored in a different directory. WOW64 hides this difference by using a file system redirector.

In most cases, whenever a 32-bit application attempts to access %windir%\System32, %windir%\lastgood\system32, or %windir%\regedit.exe, the access is redirected to an architecture-specific path.

Original Path Redirected Path for 32-bit x86 Processes Redirected Path for 32-bit ARM Processes
%windir%\System32 %windir%\SysWOW64 %windir%\SysArm32
%windir%\lastgood\system32 %windir%\lastgood\SysWOW64 %windir%\lastgood\SysArm32
%windir%\regedit.exe %windir%\SysWOW64\regedit.exe %windir%\ SysArm32\regedit.exe

Arm 32 bit and 64 bit

Arm64x PE files which contains arm64 bit code and arm64EC code togehter.
EC= emulation compatible

can use a function is IsWow64Process2
BOOL IsWow64Process2(
[in] HANDLE hProcess,
[out] USHORT *pProcessMachine,
[out, optional] USHORT *pNativeMachine
);

outputl values wil be
IMAGE_FILE_MACHINE_ARM

0x01c0

ARM Little-Endian

IMAGE_FILE_MACHINE_THUMB

0x01c2

ARM Thumb/Thumb-2 Little-Endian

IMAGE_FILE_MACHINE_ARMNT

0x01c4

ARM Thumb-2 Little-Endian

IMAGE_FILE_MACHINE_ARM64

0xAA64

ARM64 Little-Endian

from: https://learn.microsoft.com/en-us/windows/arm/arm64ec?source=recommendations
arm64ec – Arm64EC (“Emulation Compatible”) enables you to build new native apps or incrementally transition existing x64 apps to take advantage of the native speed and performance possible with Arm-powered devices, including better power consumption, battery life, and accelerated AI & ML workloads.

Arm64EC is a new application binary interface (ABI) for apps running on Arm devices with Windows 11. It is a Windows 11 feature that requires the use of the Windows 11 SDK and is not available on Windows 10 on Arm.

Interoperability
Code built as Arm64EC is interoperable with x64 code running under emulation within the same process. The Arm64EC code in the process runs with native performance, while any x64 code runs using emulation that comes built-in with Windows 11. Even if your app relies on existing dependencies or plugins that don’t yet support Arm, you can start to rebuild parts of your app as Arm64EC to gain the benefits of native performance.

Arm64EC guarantees interoperability with x64 by following x64 software conventions including calling convention, stack usage, data structure layout, and preprocessor definitions. However, Arm64EC code is not compatible with code built as Arm64, which uses a different set of software conventions.

The Windows 11 on Arm operating system itself relies heavily on Arm64EC’s interoperability to enable running x64 applications. Most operating system code loaded by an x64 app running on Windows 11 on Arm will have been compiled as Arm64EC, enabling native performance for that code without the application knowing.

An x64 or Arm64EC process can load and call into both x64 and Arm64EC binaries, whereas an Arm64 process can only load Arm64 binaries. Both architectures can load Arm64X binaries as those contain code for both x64 and Arm64.

Any x64 code, including code from dependencies, in an Arm64EC process will run under emulation in your app. Prioritizing the most CPU-intensive dependencies to transition from x64 to Arm64EC will have the greatest impact toward improving your app’s performance.

Identifying Arm64EC binaries and apps – For developers interested in identifying these binaries, you can see them in a developer command prompt using link /dump /headers

File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
8664 machine (x64) (ARM64X)

The combination of (x64) and (ARM64X) indicates that some portion of the binary has been recompiled as Arm64EC, even though the binary still appears to be x64. A binary with a machine header that contains (ARM64) and (ARM64X) is an Arm64X PE file that can be loaded into both x64 and Arm64 apps.

Windows Task Manager can also be used to identify if an app has been compiled as Arm64EC. In the Details tab of Task manager, the Architecture column will show ARM64 (x64 compatible) for applications whose main executable has been partially or completely compiled as Arm64EC.

from https://learn.microsoft.com/en-us/windows/arm/arm64ec-abi:
It also implies that any Arm64 registers which cannot be fitted into the x64 CONTEXT must not be used, as their values can be lost anytime an operation using CONTEXT occurs (and some can be asynchronous and unexpected, such as the Garbage Collection operation of a Managed Language Runtime, or an APC).

The mapping rules between Arm64EC and x64 registers are represented by the Arm64EC_NT_CONTEXT structure in the Windows headers, present in the SDK. This structure is essentially a union of the CONTEXT structure, exactly as it is defined for x64, but with an extra Arm64 register overlay.

For example, RCX maps to X0, RDX to X1, RSP to SP, RIP to PC, etc. We can also see how the registers x13, x14, x23, x24, x28, v16-v31 have no representation and, thus, cannot be used in Arm64EC.

This register usage restriction is the first difference between the Arm64 Classic and EC ABIs.

Leave a comment