Windows 11 ARM x86/x64 模拟层原理详解
2026/7/3大约 4 分钟
Windows 11 ARM x86/x64 模拟层原理详解
Windows 11 on ARM 为什么能跑 x64 程序?微软用动态二进制翻译(DBT)技术在用户态即时把 x86/x64 指令编译成 ARM64 指令。本文深入 Prism 模拟器的组件、JIT 流程、缓存机制和限制。
模拟器组件
Windows 11 ARM 内置多套模拟器(均运行在用户态):
| 组件 | 位置 | 功能 |
|---|---|---|
xtajit64.dll | C:\Windows\System32\ | x64 → ARM64 即时编译 |
xtajit64se.dll | KnownDLLs 注册 | x64 安全版本 |
wow64.dll | KnownDLLs 注册 | WoW64 核心基础设施 |
wow64win.dll | KnownDLLs 注册 | Win32k 系统调用 thunk |
在 EC2 Graviton 实例的 Windows 11 ARM 系统上验证:
xtajit64.dll存在(1,451,096 字节)SysWOW64\xtajit.dll(x86 模拟器)文件不存在wowarmhw.dll(ARM32 模拟器)文件不存在
x86 模拟可能通过 xtajit64.dll 统一处理,或该系统版本未包含完整 x86 模拟支持。
JIT 编译流程
x64 进程启动
├─ Loader 识别 PE 为 x64 格式
├─ 创建进程,映射 x64 EXE 和 DLL
├─ 加载 xtajit64.dll 到进程地址空间
└─ 初始化翻译缓存(XtaCache)
首次执行代码块
├─ CPU 执行到 x64 指令地址
├─ 触发模拟器入口(通过 Entry Thunk)
├─ xtajit64 读取 x64 指令流
├─ 解码、优化、生成 ARM64 等价代码
├─ 写入可执行内存区域
└─ 跳转到翻译后的 ARM64 代码执行
后续执行
├─ 检查缓存是否命中
├─ 命中 → 直接执行缓存的 ARM64 代码
└─ 未命中 → 触发新的 JIT 编译翻译块(Translation Block)
JIT 以基本块(basic block)为单位翻译,一个基本块是一段线性指令序列,以跳转、调用或返回结束。每个基本块被翻译成一个对应的 ARM64 代码块,写入缓存。
翻译缓存的命中率高是性能的关键。频繁执行的代码(热路径)几乎全部走缓存,只有第一次执行或缓存淘汰时才触发 JIT 编译。
系统调用 Thunk
x64 程序调用 Win32 API 时,参数和调用约定需要转换:
- x64 程序通过
wow64win.dll发起系统调用 wow64win.dll将 x64 调用约定(如寄存器传参)转换为 ARM64 调用约定- 调用真正的 ARM64 系统服务(ntdll.dll 等)
- 返回值再转换回 x64 格式
这个过程对应用透明,但会带来少量开销。GetNativeSystemInfo 等 API 在模拟进程中返回虚拟化信息,让应用"以为"自己在 x64 系统上运行。
指令集支持
Prism 支持 x86/x64 的绝大部分指令集,包括:
- 整数运算、逻辑运算、位操作
- SSE/SSE2/SSE3/SSE4(通过 NEON/SVE 模拟)
- AVX/AVX2(部分支持,性能有损耗)
依赖 AVX-512 的应用不受支持。
内核驱动限制
模拟器只作用于用户态代码:
- Windows 内核本身是 ARM64 原生编译,内核模式代码直接运行在 ARM64 CPU 上
- 内核代码运行在高 IRQL,无法切换到模拟器上下文
- 内核驱动必须通过微软 ARM64 签名验证
因此 x64 驱动(.sys)无法加载。Sysmon x64 的内核驱动 SysmonDrv.sys 加载失败就是这个原因。
性能开销来源
| 来源 | 说明 |
|---|---|
| JIT 编译开销 | 首次执行代码块时的翻译时间 |
| 缓存查找开销 | 每次执行前检查缓存 |
| Thunk 开销 | 跨架构调用时的参数转换 |
| 指令语义差异 | 某些 x64 指令需要多条 ARM64 指令模拟 |
| 内存模型差异 | 某些原子操作需要额外处理 |
典型开销:
- 计算密集型(加密、压缩):+20%~40%
- IO 密集型(文件操作、网络):+5%~15%
- 混合型:+10%~30%
诊断命令
查看进程架构分布
$x64 = 0; $arm64 = 0; $x86 = 0
Get-Process | ForEach-Object {
try {
$m = $_.MainModule.FileName
if ($m) {
$pe = [System.IO.File]::ReadAllBytes($m)
$off = [BitConverter]::ToInt32($pe, 60)
$mach = [BitConverter]::ToUInt16($pe, $off + 4)
switch ($mach) {
0x8664 { $x64++ }
0xAA64 { $arm64++ }
0x14c { $x86++ }
}
}
} catch {}
}
Write-Host "ARM64: $arm64, x64: $x64, x86: $x86"检测当前进程是否在模拟层下
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Wow64 {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool IsWow64Process2(
IntPtr hProcess,
out ushort pProcessMachine,
out ushort pNativeMachine);
}
"@
$proc = $native = 0
[Wow64]::IsWow64Process2([IntPtr](-1), [ref]$proc, [ref]$native)
Write-Host "ProcessMachine: 0x$($proc.ToString('X4')), NativeMachine: 0x$($native.ToString('X4'))"
# 0x0000 = 原生进程, 0x8664 = x64 模拟, 0x14c = x86 模拟, 0xAA64 = ARM64 原生系统验证 xtajit64.dll 存在
Get-Item C:\Windows\System32\xtajit64.dll | Select-Object FullName, Length, LastWriteTime
# Length: 1451096