1. 项目缘起为什么要在 iOS 上折腾 Wine“Madeira”这个项目标题乍一看像是一个地名但在我们这群喜欢折腾跨平台兼容层的人眼里它指向的是一件事在 iOS 设备上运行 Windows 应用程序的探索性尝试。热搜词里同时出现了 Wine、FEX-Emu、DXMT、iOS、x86-64 这几个关键词基本可以勾勒出这个项目的技术轮廓——它不是简单的“装个模拟器”而是试图在 ARM 架构的 iOS 设备上通过多层翻译与兼容机制把 x86-64 的 Windows 程序跑起来。先说清楚这个项目能做什么。简单讲它想让你的 iPhone 或 iPad 具备运行部分 Windows 软件的能力比如一些老式的小工具、轻量级游戏、或者特定行业的 Windows 客户端。它解决的核心问题是iOS 生态相对封闭无法直接安装 Windows 程序而 Madeira 试图通过 Wine 的兼容层、FEX-Emu 的指令翻译、以及 DXMT 的图形转换打通这条链路。适合谁来参考适合对 iOS 底层机制感兴趣、愿意折腾、有一定命令行基础、并且能接受“不完美兼容”的开发者或高级玩家。如果你只是想找个即装即用的方案这个项目大概率会让你失望因为它目前的状态更接近“技术验证”而非“成熟产品”。我之所以关注这个方向是因为过去几年里Wine 在 Linux 和 macOS 上的表现越来越成熟但 iOS 一直是个空白地带。不是没人想做而是 iOS 的限制太多没有 JIT 权限、沙盒机制严格、图形接口封闭。Madeira 的思路本质上是在这些限制的夹缝里找一条路。下面我会从整体设计、核心技术点、实操流程、以及踩坑经验四个维度把这个项目拆开来讲。2. 整体架构与核心思路拆解2.1 为什么是 Wine FEX-Emu DXMT 这个组合要理解 Madeira 的架构得先明白一个基本事实iOS 设备用的是 ARM 架构芯片而绝大多数 Windows 程序是为 x86-64 架构编译的。这两者之间隔着一条指令集的鸿沟。Wine 本身不是模拟器它是一个兼容层负责把 Windows 的 API 调用翻译成宿主系统的调用。但 Wine 解决的是“系统调用”层面的问题不解决“指令集”层面的问题。所以你需要一个能跑 x86-64 指令的东西这就是 FEX-Emu 的角色。FEX-Emu 是一个开源的 x86-64 模拟器专门为 ARM64 平台设计。它的特点是性能损耗相对可控而且支持 JIT 编译。但 iOS 对 JIT 有严格限制这是整个项目最大的技术难点之一。DXMT 则是负责图形层的它把 Direct3D 调用转换成 Metal 调用因为 iOS 原生只认 Metal。这三者叠在一起形成了一条完整的链路Windows 程序 → WineAPI 翻译→ FEX-Emu指令翻译→ DXMT图形翻译→ iOS 系统。为什么不用其他方案比如 QEMU 全系统模拟因为 QEMU 太重了在移动设备上性能根本扛不住。而 CrossOver 那种商业方案底层其实也是 Wine只是做了大量优化和适配。Madeira 选择这条路线核心考量是在性能、兼容性和可实现性之间找平衡。FEX-Emu 的 JIT 在 ARM 上已经有不错的实测数据DXMT 也在 macOS 上验证过可行性剩下的就是怎么在 iOS 的沙盒里把它们串起来。2.2 iOS 沙盒与 JIT 权限的现实约束这里必须泼一盆冷水。iOS 的沙盒机制决定了你不可能像在 Linux 上那样随意加载动态库、执行任意代码。每个 App 只能访问自己的沙盒目录系统调用也受到严格限制。更关键的是 JIT 权限——在没有特殊授权的情况下iOS 不允许 App 在运行时生成可执行代码。这意味着 FEX-Emu 的 JIT 功能在标准 iOS 环境下是无法直接使用的。那 Madeira 怎么绕常见的做法是利用 iOS 的“开发者模式”配合特定的签名方式或者在某些允许 JIT 的场景下运行。热搜词里出现了“ios开发者模式”和“ios 26.3.1怎么开发者模式”说明很多人卡在这一步。开启开发者模式本身不难难的是让系统允许 JIT。目前公开的路径主要有两条一是通过特定的 entitlement 签名二是利用某些系统组件的特性。但这两条路都有门槛不是普通用户能轻松搞定的。我的建议是如果你没有 iOS 开发经验先别急着上手。先把 Xcode 的证书配置、签名流程、以及开发者模式开启这些基础操作跑通再考虑 Madeira 的事。否则你会在一堆签名错误和权限拒绝里迷失方向。2.3 图形层的转换逻辑从 Direct3D 到 MetalDXMT 是这个项目里另一个关键组件。Windows 程序大量使用 Direct3D 来渲染图形而 iOS 只支持 Metal。DXMT 的作用就是在中间做转换。它的原理和 DXVK 类似DXVK 是把 D3D 转成 VulkanDXMT 是把 D3D 转成 Metal。这个转换过程涉及着色器编译、资源管理、同步机制等一系列复杂操作。在实际使用中图形层的兼容性往往是最容易出问题的地方。有些程序用的是 D3D9有些用 D3D11还有些用 D3D12。DXMT 对不同版本的支持程度不一样D3D9 和 D3D11 相对成熟D3D12 还在完善中。如果你要跑的程序依赖特定的图形特性比如某些后处理效果或者计算着色器那大概率会遇到渲染错误或者直接崩溃。我的经验是优先选择那些图形需求简单的程序来测试比如老式 2D 游戏或者工具类软件成功率会高很多。3. 核心组件详解与配置要点3.1 Wine 的编译与裁剪不是越全越好在 iOS 上跑 Wine你不能直接用官方版本。官方 Wine 是为桌面系统设计的依赖大量 iOS 上不存在的库。你需要自己编译并且做大量裁剪。裁剪的原则是只保留目标程序需要的模块把不必要的组件全部去掉。比如如果你不打算跑需要网络功能的程序就可以把 WinINet、WinHTTP 这些模块去掉能显著减小体积和复杂度。编译 Wine 的时候有几个参数需要特别注意。--enable-archs要指定为 i386,x86_64因为你要跑的是 32 位和 64 位的 Windows 程序。--without-x是必须的iOS 没有 X11。--with-coreaudio和--with-metal这些要看你的需求如果需要音频和图形支持就加上。另外Wine 的 Gecko 和 Mono 组件在 iOS 上基本用不了可以直接排除。热搜词里有人问“wine gecko官方正版下载”其实在 Madeira 这个场景下Gecko 不是必需品除非你要跑依赖 HTML 渲染的 Windows 程序。编译完成后你会得到一个包含 Wine 运行时的库文件集合。这些文件需要被打包进 iOS App 的 bundle 里并且要确保签名正确。这里有个坑Wine 的某些库文件在签名后可能会被系统拒绝加载因为它们的 entitlement 不符合要求。解决办法是逐个检查库文件的签名状态必要时重新签名。3.2 FEX-Emu 的移植与 JIT 适配FEX-Emu 的移植是整个项目里技术难度最高的部分。它原本是为 Linux ARM64 设计的要移植到 iOS需要解决几个问题一是 JIT 内存的分配iOS 不允许随意申请可执行内存二是线程和信号的处理iOS 的线程模型和 Linux 有差异三是系统调用的映射FEX-Emu 需要把 x86-64 的系统调用转换成 ARM64 的系统调用而 iOS 的系统调用接口和 Linux 又不一样。目前公开的资料里关于 FEX-Emu 在 iOS 上的移植细节并不多。根据我的经验可行的路径是先在一个允许 JIT 的环境下比如越狱设备或者特定的开发者环境验证 FEX-Emu 的基本功能然后再逐步适配 iOS 的限制。如果你没有越狱设备那就要依赖开发者模式下的特殊签名这条路更复杂但也不是完全走不通。JIT 适配的核心是内存管理。iOS 提供了mmap的变体来申请内存但默认是不可执行的。你需要用mprotect来修改内存权限而这一步在 iOS 上受到严格限制。有些开发者通过pthread_jit_write_protect_np这个 API 来动态切换内存的可写和可执行状态这是一种常见的绕过方式。但要注意这个 API 的行为在不同 iOS 版本上可能有差异需要做版本适配。3.3 DXMT 的集成与图形调试DXMT 的集成相对独立它主要和 Wine 的图形驱动层打交道。在 Wine 里图形驱动是通过winex11.drv或者winemac.drv这样的模块实现的。在 iOS 上你需要写一个wineios.drv把 DXMT 的 Metal 后端接进去。这个驱动需要实现 Wine 定义的图形接口包括窗口管理、表面创建、呈现等。调试图形问题是最耗时的。常见的现象包括画面黑屏、纹理错乱、帧率极低、程序崩溃。排查的时候首先要确认 DXMT 的日志输出看是否有着色器编译失败或者资源创建失败的记录。其次可以用 Metal 的调试工具比如 Xcode 自带的 GPU 帧捕获来分析渲染管线。如果问题出在特定的 D3D 特性上可能需要修改 DXMT 的源码来增加支持。有个实用技巧在测试阶段可以先把图形输出降到最简单的模式比如只渲染一个纯色背景确认链路通了之后再逐步增加复杂度。这样能快速定位问题出在哪个环节。4. 实操流程从零开始搭建 Madeira 环境4.1 准备工作设备、系统与工具链先说设备要求。你需要一台运行 iOS 16 或更高版本的设备最好是 A12 芯片以后的因为 FEX-Emu 对 CPU 性能有一定要求。存储空间至少留出 10GB因为 Wine 运行时加上目标程序体积不小。系统版本方面热搜词里有人问“ios 26.3.1怎么开发者模式”说明新版本系统也在尝试。我的建议是如果可能的话用 iOS 16 或 17 的版本兼容性相对好一些新系统往往有更多限制。工具链方面你需要一台 Mac用于编译和签名、Xcode最新稳定版、iOS App 签名证书开发者证书或者企业证书、以及一个能运行命令行的环境。如果你没有 Mac那这个项目基本没法推进因为 iOS App 的编译和签名离不开 Xcode。另外建议准备一个单独的测试设备不要用主力机因为过程中可能需要反复抹掉设备或者修改系统设置。4.2 编译 Wine 与 FEX-Emu 的详细步骤编译 Wine 的流程大致如下。首先从官方仓库拉取源码切换到稳定的分支。然后配置编译选项./configure --hostarm-apple-darwin \ --enable-archsi386,x86_64 \ --without-x \ --with-coreaudio \ --with-metal \ --disable-tests \ --prefix/path/to/install配置完成后执行make -j$(sysctl -n hw.ncpu)开始编译。编译过程中可能会遇到各种依赖缺失的问题需要根据报错逐个解决。编译完成后make install会把文件安装到指定目录。FEX-Emu 的编译更复杂一些。你需要先编译它的依赖比如fmt、catch2等。然后配置 FEX-Emu 本身cmake -DCMAKE_TOOLCHAIN_FILEios.toolchain.cmake \ -DCMAKE_BUILD_TYPERelease \ -DENABLE_JITON \ -DENABLE_ASSERTIONSOFF \ ..注意ENABLE_JIT要打开但实际运行时能否用 JIT 取决于 iOS 的权限。编译完成后你会得到libFEXCore等库文件。4.3 打包成 iOS App 与签名配置把 Wine 和 FEX-Emu 的库文件打包进 iOS App需要创建一个 Xcode 工程。工程里要包含一个主 App target以及必要的资源文件。Wine 的运行时文件比如wine可执行文件、wineserver、各种.dll和.so需要放在 App bundle 的特定目录下通常是Resources或者自定义的wine目录。签名是最容易出问题的环节。你需要为每个可执行文件和动态库单独签名并且要确保它们的 entitlement 一致。如果某个库文件没有签名或者签名不匹配App 启动时会直接崩溃。建议用codesign命令批量签名codesign --force --sign Your Certificate --entitlements ent.plist path/to/fileentitlements 文件里需要包含com.apple.security.cs.allow-jit和com.apple.security.cs.allow-unsigned-executable-memory这两个键否则 JIT 无法工作。但要注意这两个 entitlement 在标准开发者证书下可能不被允许需要特殊的授权。4.4 首次运行与基础测试App 安装到设备后第一次运行需要开启开发者模式。在“设置 → 隐私与安全性”里找到“开发者模式”打开并重启设备。然后信任你的开发者证书。接下来你可以通过 App 内的终端或者文件管理功能把 Windows 程序拷贝到 Wine 的虚拟 C 盘目录下。测试的时候先从最简单的程序开始比如notepad.exe或者winver.exe。这些程序不依赖复杂的图形和网络功能能跑起来就说明基础链路通了。如果notepad都跑不起来那问题大概率出在 Wine 的配置或者 FEX-Emu 的 JIT 上。检查日志的时候重点关注wine的输出和FEX的日志看是否有加载失败或者指令翻译错误的记录。5. 常见问题与排查技巧实录5.1 Wine 乱码与字体配置热搜词里“wine 乱码”和“wine 栏是乱码”出现的频率很高说明这是大家普遍遇到的问题。Wine 在 iOS 上乱码通常是因为缺少中文字体或者字体映射配置不对。解决办法是把中文字体文件比如simsun.ttc或者NotoSansCJK.ttc拷贝到 Wine 的C:\windows\Fonts目录下然后在注册表里配置字体替换。具体操作是用wine regedit打开注册表编辑器找到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes添加相应的键值。比如把MS Shell Dlg替换成Noto Sans CJK SC。另外Wine 的winecfg里也可以设置默认字体。如果乱码出现在菜单栏或者标题栏那可能是winex11.drv或者wineios.drv的字体渲染有问题需要检查驱动层的字体处理逻辑。5.2 FEX-Emu 崩溃与 JIT 权限排查FEX-Emu 崩溃的原因很多最常见的是 JIT 权限不足。如果你在日志里看到Failed to allocate JIT memory或者mprotect failed那基本可以确定是 JIT 被系统拦截了。这时候要检查 App 的 entitlement 是否正确以及开发者模式是否开启。另外某些 iOS 版本对 JIT 的限制更严格可能需要额外的配置。另一个常见问题是指令翻译错误。FEX-Emu 虽然支持大部分 x86-64 指令但某些冷门指令或者特定扩展比如 AVX-512可能没有实现。如果目标程序用到了这些指令就会崩溃。排查方法是看 FEX 的日志里是否有Unhandled instruction的记录。如果有那只能等 FEX-Emu 更新或者换一个不依赖这些指令的程序版本。5.3 图形渲染问题速查表现象可能原因排查方法解决思路黑屏DXMT 未正确初始化检查 DXMT 日志确认 Metal 设备可用检查驱动加载顺序纹理错乱着色器编译失败查看着色器编译日志更新 DXMT 版本或禁用特定图形特性帧率极低JIT 未生效或 CPU 瓶颈检查 FEX 日志和 CPU 占用确认 JIT 开启降低渲染分辨率程序崩溃D3D 特性不支持查看崩溃日志尝试切换 D3D 版本或用软件渲染画面撕裂垂直同步未开启检查呈现参数在 DXMT 配置里开启 VSync5.4 性能优化的几个实用技巧性能是 iOS 上跑 Wine 的另一个大问题。即使能跑起来帧率也可能低得没法用。优化的时候先从这几个方面入手一是关闭不必要的 Wine 服务比如wineserver的某些后台线程二是调整 FEX-Emu 的 JIT 参数比如增大代码缓存三是降低图形设置比如把分辨率降到 720p 或者更低四是关闭音频因为音频处理也会占用 CPU。另外iOS 设备的热管理很激进长时间高负载运行会降频。所以测试的时候最好给设备加个散热背夹或者间歇性运行避免过热导致性能骤降。我的实测经验是A15 芯片的设备跑一些轻量级 Windows 程序帧率能到 20-30 FPS但持续几分钟后就会因为发热降到 10 FPS 左右。所以这个方案目前更适合短时间使用不适合长时间游戏。6. 个人实操体会与后续扩展方向折腾 Madeira 这个项目最大的感受是它更像一个技术验证平台而不是一个能日常使用的工具。从 Wine 的编译裁剪到 FEX-Emu 的 JIT 适配再到 DXMT 的图形调试每一步都有大量的细节需要处理。如果你只是想跑某个特定的 Windows 程序建议先评估一下有没有更简单的替代方案比如找 iOS 原生的同类 App或者用远程桌面的方式。不过这个项目的价值在于它探索了一条路。随着 FEX-Emu 和 DXMT 的持续更新以及 iOS 系统对 JIT 限制的可能变化未来在 iOS 上跑 Windows 程序的体验会越来越好。如果你对这个方向感兴趣我建议先从 Linux 或 macOS 上的 Wine 开始练手熟悉了基本流程之后再挑战 iOS 平台。另外多关注 FEX-Emu 和 DXMT 的官方仓库它们的更新频率很高很多问题可能在新版本里已经解决了。最后分享一个小技巧在调试图形问题时可以先用WINEDEBUGd3d环境变量打开详细日志这样能看到 D3D 调用的完整流程快速定位是哪个环节出了问题。这个技巧在排查黑屏和纹理错误时特别有用。