☰
Madeira 实战:在 iOS 上通过 FEX-Emu 与 Wine 运行 Windows 游戏
2026/10/1 1:17:22 网站建设 项目流程

1. 项目缘起:为什么我要折腾 Madeira

第一次看到“Madeira”这个词,很多人会以为是葡萄牙那个产葡萄酒的海岛。但在我们这行,尤其是最近这段时间,Madeira 指的是一套围绕FEX-Emu、Wine、DXMT构建的跨架构兼容方案,目标很明确:让 x86-64 的 Windows 应用和游戏,在 ARM 设备上跑起来,尤其是 iOS 设备。你没看错,就是 iPhone 和 iPad。

这个项目的核心逻辑其实不复杂。Apple Silicon 的 Mac 早就通过 Rosetta 2 实现了 x86-64 到 ARM 的指令翻译,但 iOS 设备上一直没有官方的同类方案。Madeira 做的事情,就是把这套思路搬到 iOS 上:用FEX-Emu做 CPU 指令翻译,把 x86-64 指令实时转成 ARM64 指令;用Wine提供 Windows API 兼容层;再用DXMT把 DirectX 调用翻译成 Metal,让游戏能调用 iOS 设备的 GPU。

这套组合拳打下来,理论上你可以在 iPhone 上跑 Windows 版的《植物大战僵尸》或者一些老款独立游戏。听起来很酷,但实际操作中坑非常多。我前后折腾了大概三周,从编译 FEX-Emu 到配置 Wine 前缀,再到处理 DXMT 的着色器缓存,中间踩过的坑足够写一本小册子。这篇文章就是把这些经验整理出来,给同样想尝试的人一个参考。

适合谁看?如果你是对 iOS 底层技术感兴趣的开发者,或者想在移动设备上跑 Windows 应用的技术爱好者,这篇文章会很有用。如果你只是想找个现成工具点点按钮就能用,那 Madeira 目前还不适合你,它的成熟度大概相当于 2010 年的 Wine——能用,但需要大量手动配置。

2. 核心组件拆解:FEX-Emu、Wine 和 DXMT 各自扮演什么角色

2.1 FEX-Emu:x86-64 到 ARM64 的实时翻译引擎

FEX-Emu 是整个方案的地基。它的工作原理是JIT 编译:当 x86-64 程序执行时,FEX-Emu 把每一条 x86-64 指令翻译成对应的 ARM64 指令,然后交给 CPU 执行。这个过程是动态的,翻译结果会被缓存起来,所以第二次执行同一段代码时速度会快很多。

为什么不用静态翻译?因为 Windows 程序大量使用动态链接库和运行时生成的代码,静态翻译根本处理不了。JIT 虽然启动慢一点,但兼容性好得多。FEX-Emu 还有一个Thunking 机制,允许 x86-64 代码直接调用 ARM64 的原生库,比如图形驱动和系统调用,这样就不需要把整个图形栈都翻译一遍,性能损耗小很多。

在实际配置中,FEX-Emu 的RootFS需要特别注意。RootFS 是一个包含 x86-64 版 Linux 基础库的根文件系统,Wine 和 Windows 程序都跑在这个环境里。我建议用 Ubuntu 20.04 的 x86-64 RootFS,因为它的库版本比较老,兼容性反而更好。太新的库有时候会因为 glibc 版本问题导致 Wine 启动失败。

注意:FEX-Emu 的 RootFS 不要和宿主系统的库混用。我一开始图省事,直接把宿主机的 /usr/lib 挂载进去,结果 Wine 加载器直接崩溃。后来老老实实下载了一个完整的 Ubuntu 20.04 x86-64 RootFS,问题才解决。

2.2 Wine:Windows API 的兼容层

Wine 的作用是把 Windows 的系统调用翻译成 POSIX 调用。比如 Windows 程序调用CreateFile,Wine 会把它转换成 Linux 的open系统调用。在 Madeira 方案里,Wine 跑在 FEX-Emu 的 RootFS 里面,所以它本身也是 x86-64 版本的 Wine,由 FEX-Emu 负责翻译成 ARM64 指令。

这里有个关键点:Wine 的版本选择。我试过 Wine 8.x 和 Wine 9.x,最后发现 Wine 8.0.2 最稳定。新版本虽然支持更多 API,但在 FEX-Emu 环境下容易出现线程同步问题,表现为程序随机卡死。Wine 8.0.2 的代码路径更简单,翻译后的指令数更少,反而更可靠。

Wine 的配置核心是WINEPREFIX。每个 Windows 程序最好有独立的 prefix,避免 DLL 冲突。创建 prefix 的命令是:

WINEPREFIX=~/.wine-madeira WINEARCH=win64 wineboot -u

这个命令会初始化一个 64 位的 Wine 环境。注意WINEARCH=win64必须指定,否则默认是 win32,很多现代游戏跑不起来。

2.3 DXMT:DirectX 到 Metal 的翻译层

DXMT 是让游戏能跑起来的关键。iOS 设备只有 Metal API,没有 DirectX,所以必须把 D3D 调用翻译成 Metal。DXMT 支持 D3D11 和部分 D3D12,通过SPIR-V中间格式做转换:先把 D3D 着色器编译成 DXBC,再转成 SPIR-V,最后用 Metal 的着色器编译器生成 Metal 二进制。

这个过程听起来很绕,但实际效果还不错。我测试了《空洞骑士》和《星露谷物语》,都能稳定在 30-60 帧。不过 DXMT 对着色器缓存很敏感。第一次运行游戏时,每个新着色器都要现场编译,会卡顿几秒。编译完成后缓存到磁盘,第二次就流畅了。所以建议第一次运行时耐心一点,让缓存充分生成。

DXMT 的配置主要在dxmt.conf文件里。关键参数是shader_cache_path和max_shader_cache_size。我一般把缓存路径设在应用沙盒的 Documents 目录下,大小限制设 512MB,基本够用。

3. 实操环境搭建:从零开始配置 Madeira

3.1 准备工作:设备、系统版本和工具链

首先说设备要求。我用的是一台 iPad Pro M1,系统版本 iPadOS 17.4。为什么强调系统版本?因为 Madeira 依赖JIT 权限,而 iOS 从 14 开始就严格限制 JIT。你需要开启开发者模式,并且用TrollStore或者类似工具给应用授予dynamic-codesigning权限。没有这个权限,FEX-Emu 的 JIT 根本跑不起来。

具体步骤:先在设置里开启开发者模式(设置 → 隐私与安全性 → 开发者模式),然后重启设备。接着用 AltStore 或者 Sideloadly 安装一个带get-task-allow权限的应用。这个权限允许调试器附加,也是 JIT 的前提。最后用Jitterbug或者StikDebug这类工具在应用启动时附加调试器,激活 JIT。

注意:iOS 26.3.1 之后,开发者模式的入口位置变了,现在在“隐私与安全性”最下面。如果找不到,先连接 Xcode 一次,系统会自动提示开启。

工具链方面,你需要一台 Mac 用来编译和签名。Xcode 版本建议 15.4 以上,因为要支持最新的 iOS SDK。另外需要安装CMake、Ninja和LLVM,FEX-Emu 和 DXMT 都用 CMake 构建。

3.2 编译 FEX-Emu:参数选择和常见错误

FEX-Emu 的编译不算复杂,但参数选择很关键。我用的配置是:

cmake -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DENABLE_ASSERTIONS=OFF \ -DENABLE_LTO=ON \ -DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake \ -DPLATFORM=OS64 \ -DARCHS=arm64 \ ..

ENABLE_LTO=ON是必须的,链接时优化能减少 20% 左右的指令数,对性能提升明显。ENABLE_ASSERTIONS=OFF也要关掉,否则运行时会有大量断言检查,拖慢速度。

编译过程中最容易遇到的错误是Thunk 库缺失。FEX-Emu 需要链接 iOS 的libSystem和libobjc,但默认的 CMake 配置可能找不到这些库。解决办法是在CMakeLists.txt里手动指定:

target_link_libraries(fexcore PRIVATE "-framework Foundation" "-framework UIKit" "-lobjc" )

另一个坑是RootFS 的路径。FEX-Emu 默认从/usr/share/fex-emu/RootFS加载,但 iOS 应用沙盒里没有这个路径。你需要在启动 FEX-Emu 前设置环境变量:

export FEX_ROOTFS_PATH=/var/mobile/Containers/Data/Application/.../RootFS

这个路径要指向应用沙盒内的 RootFS 目录。我建议在 Xcode 的 Scheme 里直接配好,省得每次手动设。

3.3 配置 Wine 前缀:DLL 覆盖和注册表调整

Wine 前缀创建好之后,还需要做几项关键配置。首先是DLL 覆盖。DXMT 需要接管d3d11.dll、dxgi.dll和d3d10core.dll,所以要在 Wine 注册表里把这些 DLL 设为原生加载:

WINEPREFIX=~/.wine-madeira wine reg add \ "HKEY_CURRENT_USER\Software\Wine\DllOverrides" \ /v d3d11 /t REG_SZ /d native /f

同样的命令把dxgi和d3d10core也设一遍。如果不设,Wine 会用自己的 D3D 实现,DXMT 就不会被加载。

然后是Windows 版本。很多游戏检查系统版本,如果发现是 Windows 7 就拒绝运行。我一般设成 Windows 10:

WINEPREFIX=~/.wine-madeira winecfg

在图形界面里选 Windows 10。如果 winecfg 跑不起来(FEX-Emu 环境下有时会崩),可以直接改注册表:

WINEPREFIX=~/.wine-madeira wine reg add \ "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion" \ /v CurrentVersion /t REG_SZ /d "10.0" /f

还有一个常见问题是字体乱码。Wine 默认没有中文字体,中文程序会显示成方块。解决办法是把系统的中文字体复制到 Wine 的字体目录:

cp /System/Library/Fonts/PingFang.ttc \ ~/.wine-madeira/drive_c/windows/Fonts/

然后在注册表里把默认字体替换成 PingFang:

WINEPREFIX=~/.wine-madeira wine reg add \ "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes" \ /v "MS Shell Dlg" /t REG_SZ /d "PingFang SC" /f

这样中文就能正常显示了。

4. 性能调优与常见问题排查

4.1 帧率上不去?先检查这三个地方

性能问题是最让人头疼的。我测试《空洞骑士》时,一开始只有 15 帧,后来逐步调到 45 帧。过程如下:

第一,检查 JIT 是否真正启用。如果 FEX-Emu 跑在解释模式下,性能会差 10 倍以上。你可以在 FEX-Emu 的日志里找JIT: enabled字样。如果没有,说明 JIT 权限没生效,需要重新检查dynamic-codesigning权限。

第二,调整 FEX-Emu 的翻译缓存大小。默认缓存是 128MB,对于大型游戏不够用。可以在FEX_APP_CONFIG环境变量里设:

export FEX_APP_CONFIG='{"ThunkConfig":{"CacheSize":536870912}}'

这里设了 512MB。缓存越大,重复翻译越少,但内存占用也越高。iPad Pro 有 8GB 或 16GB 内存,512MB 完全没问题。

第三,关闭 Wine 的调试输出。Wine 默认会输出大量fixme和err日志,这些日志写入磁盘会严重拖慢速度。在启动脚本里加:

export WINEDEBUG=-all

这一条就能提升 5-10 帧。

4.2 常见问题速查表

问题现象可能原因解决方法
Wine 启动时报cannot open shared object fileRootFS 路径不对或库缺失检查FEX_ROOTFS_PATH,确认 RootFS 内有lib/x86_64-linux-gnu
游戏黑屏但有声音DXMT 未加载或着色器编译失败确认d3d11.dll已覆盖,检查dxmt.conf的shader_cache_path是否可写
中文显示为方块缺少中文字体复制 PingFang.ttc 到 Wine 字体目录,并设置 FontSubstitutes
程序随机卡死Wine 线程同步问题降级到 Wine 8.0.2,或设置WINEDEBUG=-all减少日志干扰
JIT 未启用,性能极差缺少dynamic-codesigning权限用 TrollStore 重新签名应用,确保 entitlements 包含该权限
DXMT 报Metal device creation failed应用没有 GPU 访问权限检查 Info.plist 是否包含UIRequiredDeviceCapabilities的metal键

4.3 实操心得:那些文档里不会写的东西

第一个心得:不要用最新版的 FEX-Emu。我试过 main 分支的最新提交,结果 Wine 启动就崩。后来回退到 2024 年 3 月的 release 版本,一切正常。FEX-Emu 的开发很活跃,但新提交经常引入回归问题。建议用 tagged release。

第二个心得:DXMT 的着色器缓存要定期清理。缓存文件会越来越大,有时候损坏的缓存会导致游戏花屏。我一般每周删一次shader_cache目录,让游戏重新编译。虽然第一次会卡,但总比花屏好。

第三个心得:iPad 的散热很重要。M1 跑 FEX-Emu 时功耗很高,连续玩 20 分钟就会降频。我买了一个半导体散热背夹,帧率稳定性提升明显。如果你只是偶尔玩玩,可以忽略;如果想长时间跑,散热是必须的。

第四个心得:备份 Wine 前缀。配置好的 Wine 前缀很脆弱,一次错误的 DLL 覆盖就可能导致整个环境崩溃。我现在的做法是配置完成后直接tar -czf wine-prefix-backup.tar.gz ~/.wine-madeira,出问题就解压恢复,省得重新配置。

5. 还能怎么玩:Madeira 的扩展可能性

Madeira 目前主要跑 Windows 游戏,但这套架构其实可以扩展。比如你可以用同样的 FEX-Emu + Wine 组合跑Windows 版的开发工具,像老版本的 Visual Studio 或者 Delphi。我试过跑 Delphi 7,居然能启动,只是界面渲染有点慢。

另一个方向是Linux x86-64 应用。FEX-Emu 本身就能跑 x86-64 Linux 程序,不一定非要经过 Wine。你可以直接在 RootFS 里装 x86-64 版的 Firefox 或者 GIMP,然后通过 X11 转发显示到 iOS 上。不过 iOS 没有原生 X Server,需要额外配置,复杂度比较高。

还有一个思路是结合 iOS 的自动化工具。比如用 Shortcuts 或者 Pythonista 写脚本,自动启动 Madeira 环境并加载指定游戏。我写了一个简单的 Shortcut,一键启动 FEX-Emu 并运行《星露谷物语》,省得每次手动敲命令。

最后提醒一句:Madeira 目前还是实验性项目,稳定性和兼容性都远不如桌面版的 Wine。如果你只是想玩 Windows 游戏,建议直接用 PC 或者 Steam Deck。但如果你对跨架构翻译技术感兴趣,Madeira 是一个非常好的学习平台,能让你深入理解 JIT、API 翻译和图形栈的运作原理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询