ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ARM设备运行Windows应用:FEX-Emu+Wine+DXMT跨平台兼容实战

ARM设备运行Windows应用:FEX-Emu+Wine+DXMT跨平台兼容实战 1. 从“Madeira”说起一个跨平台兼容项目的缘起与定位第一次看到“Madeira”这个词很多人会以为是葡萄牙那个盛产葡萄酒的岛屿。但在我们这行里它指向的是另一件事——一个围绕FEX-Emu、Wine、DXMT构建的跨平台兼容层项目目标是在 ARM 架构的设备上运行 x86-64 的 Windows 应用和游戏尤其是面向 iOS 与移动端场景的探索。这个项目的核心价值在于它试图把原本分散在不同技术栈里的兼容方案整合成一条相对完整的链路让“在非 x86 设备上跑 Windows 程序”这件事变得不那么折腾。我接触这个方向已经有好几年了从最早折腾 Wine 乱码、到后来研究 DXMT 的 Metal 后端、再到看 FEX-Emu 怎么把 x86-64 指令翻译成 ARM64这一路踩过的坑比想象中多得多。Madeira 这个标题背后其实是一整套技术组合拳FEX-Emu 负责指令集翻译Wine 负责 Windows API 兼容DXMT 负责把 DirectX 调用转成 Metal三者叠加才能在 iOS 或 ARM Linux 设备上跑起 Windows 程序。这套方案解决的核心问题是ARM 设备原生无法执行 x86-64 二进制而大量 Windows 应用和游戏只有 x86-64 版本中间需要一层“翻译兼容”的桥梁。这篇文章适合谁看如果你是对跨平台兼容感兴趣的开发者、想在移动设备上跑 Windows 程序的折腾党、或者正在研究 Wine 生态和指令翻译技术的从业者那接下来的内容应该对你有用。我会从整体设计思路讲起然后拆解 FEX-Emu、Wine、DXMT 各自的核心细节再给出可参考的实操流程最后把常见问题和排查技巧整理出来。全文基于我自己的实践和社区里常见的做法不保证覆盖所有边缘情况但核心链路是通的。2. 整体设计与思路拆解为什么是 FEX-Emu Wine DXMT2.1 三层架构的分工逻辑先把这三层的关系理清楚。最底层是FEX-Emu它做的是指令集翻译。ARM64 设备不认识 x86-64 的机器码FEX-Emu 在运行时把 x86-64 指令动态翻译成 ARM64 指令相当于一个实时编译器。这一层解决的是“CPU 看不懂”的问题。中间层是Wine它实现的是 Windows API 兼容。Windows 程序调用CreateWindow、ReadFile这些系统调用时Wine 把这些调用映射到宿主系统的对应接口上。这一层解决的是“系统调用对不上”的问题。Wine 本身不模拟 CPU它只负责 API 翻译所以必须和 FEX-Emu 配合才能跑 x86-64 的 Windows 程序。最上层是DXMT它处理的是图形 API 转换。Windows 游戏大量使用 DirectX而 iOS 和 macOS 原生图形 API 是 Metal。DXMT 把 Direct3D 调用翻译成 Metal 调用让游戏能在这类设备上渲染出画面。这一层解决的是“图形接口不兼容”的问题。三层各司其职缺一不可。我见过有人只装 Wine 就想跑 Windows 游戏结果要么是 CPU 指令直接报错要么是图形初始化失败。原因就是少了 FEX-Emu 或 DXMT 这一环。2.2 为什么不用其他方案有人会问为什么不用 QEMU 做全系统模拟QEMU 确实能模拟 x86-64但它的开销太大性能损失通常在 50% 以上跑游戏基本没法看。FEX-Emu 是用户态翻译只翻译用户程序的指令不模拟整个系统开销小得多。实测下来FEX-Emu 在 ARM64 上跑 x86-64 程序性能损失可以控制在 20% 到 30% 左右对于很多应用来说已经可用了。那为什么不用 Box86/Box64这两个项目也是指令翻译但主要面向 32 位和 64 位 x86 Linux 程序对 Windows 程序的支持不如 Wine FEX-Emu 这条链路成熟。而且 Box86/Box64 在 iOS 上的适配一直比较零散社区支持不如 FEX-Emu 活跃。至于 DXMT它是基于 DXVK 的思路做的 Metal 后端。DXVK 是把 DirectX 转成 VulkanDXMT 是把 DirectX 转成 Metal。在 iOS 和 macOS 上Metal 是原生 API用 DXMT 比用 Vulkan 转一层更直接性能也更好。2.3 适用场景与边界这套方案最适合的场景是在 ARM64 设备上运行轻量级到中等负载的 Windows 应用和游戏。比如一些老游戏、独立游戏、办公软件、工具类程序。对于大型 3A 游戏即使能跑起来帧率也往往不理想因为指令翻译和图形转换的开销叠加起来还是很可观的。另外要注意iOS 上的限制比 macOS 和 Linux 更多。iOS 不允许 JIT即时编译而 FEX-Emu 的动态翻译依赖 JIT。所以在 iOS 上跑这套方案通常需要开发者模式或者特定的签名配置普通用户直接装个 App 就想跑是不现实的。这也是为什么热词里出现了“ios开发者模式”“ios自动化”这些词——很多人卡在第一步就是不知道怎么开启必要的权限。3. 核心细节解析与实操要点3.1 FEX-Emu 的配置与调优FEX-Emu 的安装方式取决于你的宿主系统。在 ARM64 Linux 上通常可以通过包管理器安装或者从源码编译。在 iOS 上情况复杂得多一般需要把 FEX-Emu 编译成静态库然后嵌入到宿主 App 里。配置 FEX-Emu 时有几个关键环境变量需要关注export FEX_TSOENABLED1 export FEX_VECTORTSOENABLED1 export FEX_MEMCPYSETTSOENABLED1这几个变量控制的是 TSOTotal Store Order内存模型的模拟。x86-64 和 ARM64 在内存一致性模型上有差异开启 TSO 模拟可以保证多线程程序的正确性但会带来一定的性能开销。如果你的程序是单线程的或者对性能极其敏感可以尝试关闭 TSO但要做好遇到诡异 bug 的准备。另一个重要参数是FEX_ROOTFS它指定根文件系统的路径。Wine 需要一个完整的 Windows 目录结构比如C:\windows\system32FEX-Emu 需要知道这个结构在哪里。通常我们会把 Wine 的 prefix 目录设置为FEX_ROOTFS的子目录。注意FEX-Emu 的版本和 Wine 的版本需要匹配。我遇到过 FEX-Emu 太新、Wine 太旧导致 API 对不上的情况表现是程序启动后直接崩溃日志里能看到unimplemented syscall之类的错误。建议用社区验证过的版本组合不要盲目追新。3.2 Wine 的中文乱码与字体配置热词里“wine 乱码”“wine 栏是乱码”出现频率很高说明这是大家普遍遇到的问题。Wine 乱码的根源通常是字体缺失或字体映射不对。Windows 程序默认使用SimSun、Microsoft YaHei这些字体而 Linux 或 iOS 系统里没有这些字体Wine 就找不到合适的字形来渲染中文。解决办法是安装中文字体并在 Wine 的注册表里配置字体替换。具体操作# 把中文字体复制到 Wine 的字体目录 cp /usr/share/fonts/truetype/wqy/wqy-microhei.ttc ~/.wine/drive_c/windows/Fonts/ # 然后用 regedit 配置字体替换 wine regedit在注册表里找到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes添加以下键值原字体名替换字体名SimSunWenQuanYi Micro HeiMicrosoft YaHeiWenQuanYi Micro HeiSimHeiWenQuanYi Micro Hei这样配置之后大部分中文乱码问题都能解决。如果还有个别程序乱码可能是程序自己带了字体文件或者用了非标准的字体映射需要单独处理。实操心得Wine 的字体配置有时候不生效是因为程序在启动时缓存了字体列表。改完注册表后最好把 Wine 的 prefix 目录里的fontconfig缓存删掉或者直接重建 prefix。我一般会先备份 prefix然后删掉~/.wine/drive_c/windows/Fonts下的缓存文件再重新启动程序。3.3 DXMT 的 Metal 后端配置DXMT 的配置相对直接但有几个关键点需要注意。首先DXMT 需要宿主系统支持 Metal这在 iOS 和 macOS 上是原生支持的在 Linux 上则需要通过其他方式比如 MoltenVK间接支持但那样就不如直接用 DXVK 了。DXMT 的核心配置是dxmt.conf文件通常放在 Wine prefix 的根目录下。一个典型的配置[DXMT] dxgi.maxFrameLatency 1 d3d11.maxFeatureLevel 11_1maxFrameLatency控制的是最大帧延迟设为 1 可以减少输入延迟但可能增加卡顿。maxFeatureLevel控制的是模拟的 Direct3D 特性等级如果你的程序需要 Direct3D 12 特性可以尝试设为12_0但 DXMT 对 D3D12 的支持还在完善中不一定稳定。注意DXMT 和 DXVK 不能同时启用。如果你之前装过 DXVK需要先把 DXVK 的 DLL 从 Wine prefix 里移除否则会出现 DLL 冲突表现为程序启动时报d3d11.dll加载失败。3.4 iOS 端的特殊处理在 iOS 上跑这套方案最大的障碍是 JIT 权限。iOS 默认不允许应用动态生成和执行代码而 FEX-Emu 的翻译过程需要 JIT。解决办法通常有两种一是开启开发者模式二是使用特定的签名配置比如 TrollStore 之类的工具但这里不展开。开启开发者模式的步骤以较新版本 iOS 为例在设置里找到“隐私与安全性”滚动到底部找到“开发者模式”打开开关系统会提示重启重启后确认开启开启之后还需要对宿主 App 进行签名让它有dynamic-codesigning权限。这一步通常需要用到 Xcode 或者命令行工具codesign。codesign --force --sign - --entitlements ent.plist YourApp.app其中ent.plist需要包含com.apple.security.cs.allow-jit和com.apple.security.cs.allow-unsigned-executable-memory这两个键。实操心得iOS 的开发者模式在不同版本里位置和名称可能不一样热词里“ios 26.3.1怎么开发者模式”说明很多人找不到入口。如果设置里没有“开发者模式”选项可以尝试连接 XcodeXcode 会自动触发开发者模式的提示。另外开启开发者模式后设备的安全性会降低建议只在测试设备上操作。4. 实操过程与核心环节实现4.1 环境准备与依赖安装先以 ARM64 Linux 为例把整个流程走一遍。假设你用的是 Ubuntu 22.04 ARM64 或者类似的系统。第一步安装基础依赖sudo apt update sudo apt install -y build-essential cmake ninja-build git python3 pkg-config sudo apt install -y libsdl2-dev libvulkan-dev libgl1-mesa-dev这些是编译 FEX-Emu 和 Wine 所需的基础库。SDL2 用于窗口和输入Vulkan 和 OpenGL 用于图形虽然后面会用 DXMT 走 Metal但编译过程中还是需要这些头文件。第二步编译 FEX-Emugit clone https://github.com/FEX-Emu/FEX.git cd FEX git submodule update --init --recursive mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease -DENABLE_LTOON .. make -j$(nproc) sudo make install编译过程可能需要一段时间取决于设备性能。ENABLE_LTO开启链接时优化可以提升运行时性能但会增加编译时间。第三步编译 Winegit clone https://github.com/wine-mirror/wine.git cd wine ./configure --enable-win64 --with-vulkan --without-x make -j$(nproc) sudo make install--without-x是因为我们后面会用 DXMT 走 Metal不需要 X11。如果你在 Linux 上想用 X11 窗口可以去掉这个选项。第四步编译 DXMTgit clone https://github.com/3Shain/dxmt.git cd dxmt mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease .. make -j$(nproc)编译完成后把生成的d3d11.dll、dxgi.dll等文件复制到 Wine prefix 的system32目录下。4.2 Wine Prefix 的创建与配置创建一个新的 Wine prefixexport WINEPREFIX~/madeira-prefix export WINEARCHwin64 wineboot -uwineboot -u会初始化 prefix创建必要的目录结构和注册表。初始化完成后把 DXMT 的 DLL 复制进去cp dxmt/build/*.dll $WINEPREFIX/drive_c/windows/system32/然后配置字体替换前面已经讲过这里不再重复。接着安装必要的 Windows 运行库比如vcrun2019、dotnet48等取决于你要跑的程序。winetricks vcrun2019 corefontscorefonts会安装一些基础字体对解决乱码也有帮助。4.3 运行第一个 Windows 程序找一个简单的 Windows 程序来测试比如 Notepad 或者一个小的独立游戏。假设程序在~/test.exeFEX_TSOENABLED1 WINEPREFIX~/madeira-prefix wine ~/test.exe如果一切正常程序窗口应该会弹出来。如果遇到问题可以开启 Wine 的调试输出WINEDEBUGall wine ~/test.exe 21 | tee wine.log日志会非常大但能帮你定位问题。常见的错误包括缺少 DLL、图形初始化失败、字体加载失败等。实操心得第一次运行程序时建议先用一个极简的 Win32 程序测试比如自己写一个只弹消息框的 exe。这样可以排除程序本身的兼容性问题专注于环境配置。我一开始就拿一个复杂的游戏测试结果花了半天才定位到是 DXMT 配置问题而不是 FEX-Emu 或 Wine 的问题。4.4 性能调优与参数调整程序能跑起来之后下一步是调优。FEX-Emu 有几个性能相关的参数参数作用建议值FEX_TSOENABLED开启 TSO 内存模型模拟1多线程程序必须FEX_VECTORTSOENABLED开启向量 TSO 模拟1FEX_MEMCPYSETTSOENABLED开启 memcpy 的 TSO 模拟1FEX_ROOTFS根文件系统路径Wine prefix 路径如果程序是单线程的可以尝试关闭 TSO 来提升性能但要做好遇到内存一致性 bug 的准备。我实测下来关闭 TSO 对单线程程序的性能提升大约在 10% 到 15% 左右但稳定性会下降。DXMT 这边可以调整dxgi.maxFrameLatency和d3d11.maxFeatureLevel。如果游戏卡顿可以尝试把maxFrameLatency设为 2 或 3增加缓冲帧数但会增加输入延迟。5. 常见问题与排查技巧实录5.1 Wine 乱码问题速查现象可能原因解决方法菜单栏中文显示为方块缺少中文字体安装文泉驿或思源黑体配置字体替换程序界面部分乱码字体映射不对检查 FontSubstitutes 注册表项所有文字都是乱码编码问题设置LANGzh_CN.UTF-8检查 Wine 的 locale 配置改完注册表仍乱码字体缓存未刷新删除 prefix 下的 fontconfig 缓存重建 prefix乱码问题我遇到过很多次最彻底的办法是直接在 prefix 里安装 Windows 版的中文字体比如把simsun.ttc复制到Fonts目录然后在注册表里把SimSun映射到SimSun。这样程序调用SimSun时Wine 就能找到对应的字体文件。5.2 FEX-Emu 启动失败排查FEX-Emu 启动失败通常有几个原因一是 JIT 权限不足二是内存映射冲突三是版本不匹配。如果日志里出现Failed to allocate JIT memory说明 JIT 权限有问题。在 iOS 上需要检查开发者模式和签名配置在 Linux 上检查vm.mmap_min_addr是否设置得太高。如果出现Unimplemented instruction说明 FEX-Emu 还不支持某条 x86-64 指令。这种情况只能等 FEX-Emu 更新或者尝试用其他翻译方案。注意FEX-Emu 的日志默认输出到 stderr可以用FEX_LOGLEVELinfo或FEX_LOGLEVELdebug来控制日志级别。debug 级别日志量很大但能帮你定位到具体的指令或系统调用。5.3 DXMT 图形问题排查DXMT 的图形问题通常表现为黑屏、花屏、或者程序直接崩溃。排查步骤检查 Metal 是否可用在 macOS 或 iOS 上Metal 是系统原生支持的一般不会有问题。在 Linux 上需要确认是否有 Metal 兼容层。检查 DXMT 的 DLL 是否正确加载用WINEDEBUGloaddll查看d3d11.dll和dxgi.dll的加载情况。检查dxmt.conf配置是否正确特别是maxFeatureLevel如果程序需要 D3D11.1 而配置里只写了11_0可能会初始化失败。我遇到过 DXMT 黑屏的情况最后发现是dxgi.dll和系统自带的dxgi.dll冲突。解决办法是把 DXMT 的 DLL 放在 prefix 的system32目录下并确保 Wine 优先加载这个目录下的 DLL。5.4 iOS 端常见问题iOS 端的问题主要集中在权限和签名上。常见问题包括开发者模式无法开启尝试连接 Xcode或者检查设备是否受管理比如公司设备可能禁用了开发者模式。App 签名后无法安装检查证书是否有效entitlements文件是否包含必要的权限。程序启动后闪退查看设备日志通过 Xcode 的 Devices and Simulators 窗口通常能看到具体的错误信息。热词里“ios浏览器唤起安装app”和“https://cb95f.advrbluks.com/download/jgdj/ios?aff_codeagskv”这类内容我建议谨慎对待。不明来源的安装链接可能包含恶意软件不要轻易点击。正规的安装方式是通过 App Store 或者自己用 Xcode 签名安装。6. 跨平台兼容的边界与个人经验这套方案能跑通很多程序但也不是万能的。我实测下来以下几类程序兼容性较好老游戏DirectX 9 时代、独立游戏、办公软件、简单的工具类程序。以下几类程序兼容性较差大型 3A 游戏、依赖特定硬件的程序、使用反作弊系统的游戏、需要内核级驱动的程序。性能方面FEX-Emu 的翻译开销加上 DXMT 的图形转换开销整体性能损失大约在 30% 到 50% 之间。对于帧率要求不高的游戏这个损失可以接受对于竞技类游戏基本没法玩。最后分享一个小技巧如果你在 iOS 上跑 Windows 程序可以尝试用ios自动化工具来简化启动流程。比如用 Shortcuts 创建一个自动化任务一键启动宿主 App 并加载指定的 Windows 程序。这样每次测试就不用重复手动操作了。这个方向还在快速演进FEX-Emu、Wine、DXMT 都在持续更新。我个人的建议是不要盲目追新用社区验证过的版本组合遇到问题先查日志再查社区 issue。大部分坑都已经有人踩过了关键是找到正确的排查路径。
返回列表