Win11 ARM64深度解析
【x86和x64模拟基本原理】
x86模拟从Win10 1709 Build 16299就开始支持了,它使用xtajit.dll即时翻译器和WOW64机制,SysWOW64包含了完整的WOW64运行时,部分系统DLL编译为可选的CHPE版本并放在SyChpe32下面,CHPE版本包含混合的x86/ARM64机器码,可以加速系统文件的执行
x64模拟从Win11 21H2 Build 22000(Win10 Insider Build 21277)开始支持,它使用xtajit64.dll即时翻译器和变色龙PE机制,System32大部分用户态系统文件改成了ARM64X版本,ARM64X版本复合了ARM64 Native版本和ARM64EC版本,ARM64EC版本包含混合的x64/ARM64机器码,以支持x64程序运行
【Visual C++支持编译的可执行文件版本】
- 标准PE32(+)二进制
- x86
- x64
- ARM64 Native
- ARM Thumb-2
- 混合/变色龙PE32(+)二进制
- CHPE:x86二进制(包含ARM64和x86机器码)(需要EWDK编译)
- ARM64EC:x64二进制(包含ARM64和x64机器码)
- ARM64X:ARM64 Native二进制(包含ARM64EC(x64兼容)变色龙模式)
【程序需要编译为什么版本】
一般的程序,一般建议编译x86、x64、ARM64 Native版本:
- x86 -> x86、x64、ARM64系统
- x64 -> x64、ARM64(Win11+)系统
- ARM64 Native -> ARM64系统
- 如果对与x64共享二进制组件有需求,或无法编译为ARM64 Native,而且仅需要优化性能,可以考虑ARM64EC
安装到System32的,或注册为COM/ActiveX组件的,或注册为注入其它进程的DLL,一般需要编译x86、x64、ARM64X版本:
- 在x86系统:x86 -> System32
- 在x64系统:x64 -> System32,x86 -> SysWOW64
- 在ARM64系统:ARM64X -> System32,x86 -> SysWOW64
如果不能编译为ARM64X版本,可以使用最新版本MSVC的Visual C++ ARM64生成工具生成一个ARM64X纯转发DLL,具体方法见下文
驱动程序(包含UMDF和打印机驱动)一般需要编译x86、x64、ARM64 Native版本,在对应的系统使用对应的版本
EXE编译为ARM64X有特殊效果:ARM64 Native程序运行它,系统会运行它内部的ARM64 Native版本,x64/ARM64EC程序运行它,系统会运行它内部的ARM64EC版本,Win11 22H2支持使用start /machine arm64和start /machine amd64指定运行哪一版本(Win11 21H2只能使用当前未文档化的WinAPI)
【如何判断系统是不是Win11 ARM64】
判断系统是不是Win11 ARM64可分为两步:判断系统是不是Win11和判断系统是不是ARM64
判断系统是不是Win11有很多种方法;Win11最早的版本是10.0.22000,可以用ntdll.dll的RtlGetVersion或RtlVerifyVersionInfo判断,它们是GetVersionEx或VerifyVersionInfo的替代品,区别只是返回值改成了NTSTATUS,可使用NT_SUCCESS(x)转换为BOOL,不建议使用后两者,它们被弃用(deprecated)了,而且它的行为依赖于manifest的设置;也可以判断kernel32.dll有没有GetMachineTypeAttributes这个API;虽然有风险,但是事实上也可以使用ntdll.dll的RtlNtGetVersionNumbers这个未文档化的API
判断系统是不是ARM64不能使用kernel32.dll中的GetNativeSystemInfo(原因见本文最后的测试结果)或RuntimeInformation.OSArchitecture,可以使用kernel32.dll中的IsWow64Process2:
WINBASEAPIBOOLWINAPIIsWow64Process2( _In_ HANDLE hProcess, _Out_ USHORT* pProcessMachine, _Out_opt_ USHORT* pNativeMachine );
返回TRUE表示成功,第一个参数可以是GetCurrentProcess(),也可以是有PROCESS_QUERY_INFORMATION或PROCESS_QUERY_LIMITED_INFORMATION权限的进程句柄,第二个参数如果是64位进程会返回IMAGE_FILE_MACHINE_UNKNOWN(0),如果是32位进程则返回进程的体系结构;第三个参数返回系统的原生体系结构,常见体系结构值如下:
- IMAGE_FILE_MACHINE_I386(0x14C)【x86,32位】
- IMAGE_FILE_MACHINE_AMD64(0x8664)【x64,64位】
- IMAGE_FILE_MACHINE_ARMNT(0x1C4)【ARM,32位】
- IMAGE_FILE_MACHINE_ARM64(0xAA64)【ARM64,64位】
如果需要兼容老系统,C/C++中可以使用GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process2")获取WinAPI入口点,.NET中可以通过处理EntryPointNotFoundException实现
【Visual C++运行时和.NET运行时】
Visual C++最新版运行时将ARM64运行时升级为ARM64X运行时,以同时兼容x64/ARM64EC和ARM64 Native程序:
- VS15.9和VC14.16是第一个包含ARM64运行时的VS的VC版本
- VS16.11和VC14.29是第一个包含ARM64X运行时的VS的VC版本(x64安装程序包含x64和ARM64X的DLL)
- EWDK22000包含VC14.28,运行时DLL升级为ARM64X,但x64安装程序没有更新
- Win11 ARM64在C:\Windows\apppatch\VCRedistARM64XConflictPrevention\VC_redist.arm64.exe放置了一个ARM64X运行时的安装程序,用于替代老版本的x64/ARM64运行时安装程序以避免安装冲突,这个功能是21318引入的,这个机制有一定的缺陷,因此不建议依赖老版本的运行时
.NET Framework在Win10 1903以后已经停留在版本4.8停止更新,在Win10 ARM64只有x86版本,在Win11 21H2 ARM64只有x86和x64版本,在Win11 22H2更新为版本4.8.1,Win11 22H2 ARM64增加ARM64 Native版本。
.NET (Core)在Win11 ARM64系统上共存的方式为arm64(ARM64 Native)版本安装到%programfiles%\dotnet,x64版本安装到%programfiles%\dotnet\x64,默认运行arm64版本,使用dotnet -a x64运行x64版本,为了避免安装路径冲突,建议安装如下版本的新版x64/arm64运行时或SDK,不要安装老版本:
- 3.1.21(x64)运行时、3.1.415(x64)SDK
5.0.12(x64/arm64)运行时、5.0.403(x64/arm64)SDK(已停止支持)- 6.0.0(x64/arm64)运行时和SDK
.NET Support for macOS 11 and Windows 11 for Arm64 and x64 · Issue #22380 · dotnet/sdk (github.com)
【ARM64EC格式和ARM64X格式的技术细节】
ARM64EC:包含ARM64指令的x64可执行文件
- 表现为x64程序,但可以混合x64和ARM64指令的模块
- 默认链接的静态库是ARM64指令的,因此不能在x64系统运行
- 不能在Win10 ARM64运行,只能在Win11 ARM64运行
- 二进制特征:
- 机器类型为x64程序(0x8664)
- 包含.a64xrm和.hexpthk两个节
- IMAGE_DATA_DIRECTORY_LOAD_CONFIG包含非空的CHPEMetadataPointer(ULONGLONG偏移200,虚拟地址)字段
- 加入的编译器开关:/arm64EC /D _AMD64_ /D AMD64 /D _ARM64EC_ /D ARM64EC
- 加入的链接器开关:/machine:arm64ec softintrin.lib
ARM64X:在原生ARM64可执行文件的基础上复合了ARM64EC版本的可执行文件
- 默认在原生ARM64模式执行,但被x64程序调用时会变为ARM64EC可执行文件,执行复合进去的ARM64EC版本
- 大多数用户态系统文件编译为这种格式
- 在Win10 ARM64下面可以被识别为原生ARM64可执行文件,因此兼容Win10 ARM64
- ARM64EC项目属性高级可设置编译为ARM64X
- 二进制特征:
- 机器类型为ARM64程序(0xaa64)
- 包含.a64xrm和.hexpthk两个节
- IMAGE_DATA_DIRECTORY_LOAD_CONFIG包含非空的CHPEMetadataPointer(ULONGLONG偏移200,虚拟地址)、DynamicValueRelocTableOffset(ULONG偏移224)和DynamicValueRelocTableSection字段(USHORT偏移228)
- 加入的链接器开关:/machine:arm64x softintrin.lib
- 可选链接器开关:
- /def: /defarm64native: 对ARM64EC和ARM64版本指定不同def文件
关于ARM64EC,可以参考:
Arm64EC for Windows 11 apps on Arm | Microsoft Docs
Get started with Arm64EC apps for Windows 11 on Arm | Microsoft Docs
Understanding Arm64EC ABI and assembly code | Microsoft Docs
Overview of ARM64EC ABI conventions | Microsoft Docs
关于ARM64X,可以参考:
Arm64X PE Files | Microsoft Docs
Build Arm64X Files | Microsoft Docs
【生成ARM64X纯转发DLL】
在Win11 ARM64中,x64/ARM64EC和ARM64程序均想使用同一个全局DLL名称foo.dll,但是你只有单独的x64版本(或ARM64EC版本)以及ARM64版本,但是并没有ARM64X版本,为了支持Win11 ARM64,你需要将x64版本(或ARM64EC版本)重命名为foo_x64.dll,ARM64版本重命名为foo_arm64.dll,然后使用下面的方法生成适用于Win11 ARM64的foo.dll
1、确保安装了Visual C++ ARM64生成工具
2、编写foo_x64.def
EXPORTS MyAPI1 = foo_x64.MyAPI1 MyAPI2 = foo_x64.MyAPI2
3、编写foo_arm64.def
EXPORTS MyAPI1 = foo_arm64.MyAPI1 MyAPI2 = foo_arm64.MyAPI2
4、准备一个空白的empty.cpp
5、使用下面的命令构建ARM64X纯转发DLL
:: 使用真实安装路径替代下面的路径"C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Auxiliary\Build\vcvarsamd64_arm64.bat"cl /c /Foempty_arm64.obj empty.cppcl /c /arm64EC /Foempty_x64.obj empty.cpplink /lib /machine:x64 /def:foo_x64.def /out:foo_x64.liblink /lib /machine:arm64 /def:foo_arm64.def /out:foo_arm64.liblink /dll /noentry /machine:arm64x /defArm64Native:foo_arm64.def /def:foo_x64.def empty_arm64.obj empty_x64.obj /out:foo.dll foo_arm64.lib foo_x64.lib
【关于ARM32 WOW64支持】
Windows 10/11 ARM64的ARM32支持主要用于以下应用程序:
- UWP Apps ARM32版(早期主要用于Win10Mobile[1507,1511-1703,1709(15254)]、Win10IoTCore[1507-1607,1703-1809])
- Win32应用程序ARM32版(早期主要用于Win10IoTCore[1703-1809]、NanoServer[1709-1809])
不支持以下应用程序:
- Windows RT、RT 8.1 Apps
- Office 2013 RT
- Windows Phone Silverlight Apps
- Windows Phone 8.1 Windows Runtime Apps
现在ARM32支持已经被ARM废弃,Apple Sillicon M1和ARMv9-A中的Cortex-X2和Cortex-A510已不支持运行ARM32程序(Cortex-A710仍然支持),以后很可能大多数ARM64处理器都不再支持运行ARM32程序,再加上可以运行第三方Win32程序的ARM32版本Windows只有少数几个小众的版本,并且均已停止开发,因此对于大多数开发者来说,已经没必要编译ARM32版本的Win32程序了
Win11 21H2和之前的系统始终使用CPU的AArch32模式运行ARM32应用程序,在Apple Sillicon M1和ARMv9-A中的Cortex-X2和Cortex-A510等不支持AArch32模式的新处理器中ARM32应用程序会闪退,Win11 22H2和以后的系统会报不支持该应用程序而不是闪退
【x86/x64/ARM64等多体系结构支持有关的WinAPI】
早期API:GetSystemInfo
传统API(WinXP、Win2003):
- GetNativeSystemInfo
- IsWow64Process
- GetSystemWow64Directory(Win2003)
- Wow64DisableWow64FsRedirection(Win2003)
- Wow64RevertWow64FsRedirection(Win2003)
Win10新API(Win10 1709 Build 16299):
- IsWow64Process2
- IsWow64GuestMachineSupported
- GetSystemWow64Directory2(kernelbase.dll)
- Wow64SetThreadDefaultGuestMachine(kernelbase.dll,当前docs没有,但是出现在头文件)
Win11新API(Win11):
- GetMachineTypeAttributes
- GetProcessInformation
- ProcessMachineTypeInfo
- PROCESS_MACHINE_INFORMATION
- CreateProcess
- InitializeProcThreadAttributeList
- UpdateProcThreadAttributes
- PROC_THREAD_ATTRIBUTE_MACHINE_TYPE(当前docs没有,但是出现在头文件)
- DeleteProcThreadAttributeList
【各种WinAPI在Win10/11 ARM64不同类型进程下的返回值测试结果】
Windows 10 Version 1903(1909)/2004(20H2/21H1/21H2) ARM64测试结果:
- ARM应用返回值:
- GetSystemInfo:5(ARM)
- GetNativeSystemInfo:12(ARM64)
- IsWow64Process:0
- IsWow64Process2:0x01c4(ARMNT)、0xaa64(ARM64)
- GetProcessInformation:0x0000、0
- GetSystemWow64DirectoryA=C:\windows\SysArm32
- ARM64应用返回值:
- GetSystemInfo:12(ARM64)
- GetNativeSystemInfo:12(ARM64)
- IsWow64Process:0
- IsWow64Process2:0x0000(UNKNOWN)、0xaa64(ARM64)
- GetProcessInformation:0x0000、0
- GetSystemWow64DirectoryA=C:\windows\SysWOW64
- x86应用返回值:
- GetSystemInfo:0(INTEL)
- GetNativeSystemInfo:0(INTEL)
- IsWow64Process:0
- IsWow64Process2:0x014c(I386)、0xaa64(ARM64)
- GetProcessInformation:0x0000、0
- GetSystemWow64DirectoryA=C:\windows\SysWOW64
- 通用返回值:
- IsWow64GuestMachineSupported:0x014c(I386)、0x01c4(ARMNT)
- GetSystemWow64Directory2A:
- 0x0001(TARGET_HOST):C:\windows\system32
- 0x014c(I386):C:\windows\SysWOW64
- 0x01c4(ARMNT):C:\windows\SysArm32
0x8664(AMD64):C:\windows\SysX86640xaa64(ARM64):C:\windows\SysArm64
Windows 11 ARM64测试结果:
- ARM应用返回值:
- GetSystemInfo:5(ARM)
- GetNativeSystemInfo:12(ARM64)
- IsWow64Process:1
- IsWow64Process2:0x01c4(ARMNT)、0xaa64(ARM64)
- GetProcessInformation:0x01c4(ARMNT)、5(UserEnabled+Wow64Container)
- GetSystemWow64DirectoryA=C:\windows\SysArm32
- ARM64应用返回值:
- GetSystemInfo:12(ARM64)
- GetNativeSystemInfo:12(ARM64)
- IsWow64Process:0
- IsWow64Process2:0x0000(UNKNOWN)、0xaa64(ARM64)
- GetProcessInformation:0xaa64(ARM64)、3(UserEnabled+KernelEnabled)
- GetSystemWow64DirectoryA=C:\windows\SysWOW64
- x64/ARM64EC应用返回值:
- GetSystemInfo:9(AMD64)
- GetNativeSystemInfo:9(AMD64)
- IsWow64Process:0
- IsWow64Process2:0x0000(UNKNOWN)、0xaa64(ARM64)
- GetProcessInformation:0x8664(AMD64)、1(UserEnabled)
- GetSystemWow64DirectoryA=C:\windows\SysWOW64
- x86应用返回值:
- GetSystemInfo:0(INTEL)
- GetNativeSystemInfo:9(AMD64)
- IsWow64Process:1
- 兼容性设置隐藏x64支持后:
- GetNativeSystemInfo:0(INTEL)
- IsWow64Process:0
- IsWow64Process2:0x014c(I386)、0xaa64(ARM64)
- GetProcessInformation:0x014c(I386)、5(UserEnabled+Wow64Container)
- GetSystemWow64DirectoryA=C:\windows\SysWOW64
- 通用返回值:
- IsWow64GuestMachineSupported:0x014c(I386)、0x01c4(ARMNT)
- GetSystemWow64Directory2A:
- 0x0001(TARGET_HOST):C:\windows\system32
- 0x014c(I386):C:\windows\SysWOW64
- 0x01c4(ARMNT):C:\windows\SysArm32
- GetMachineTypeAttributes:见上文GetProcessInformation