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 程序 → Wine(API 翻译)→ 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 到 Metal
DXMT 是这个项目里另一个关键组件。Windows 程序大量使用 Direct3D 来渲染图形,而 iOS 只支持 Metal。DXMT 的作用就是在中间做转换。它的原理和 DXVK 类似,DXVK 是把 D3D 转成 Vulkan,DXMT 是把 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 --host=arm-apple-darwin \ --enable-archs=i386,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_FILE=ios.toolchain.cmake \ -DCMAKE_BUILD_TYPE=Release \ -DENABLE_JIT=ON \ -DENABLE_ASSERTIONS=OFF \ ..注意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 配置里开启 VSync |
5.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 的官方仓库,它们的更新频率很高,很多问题可能在新版本里已经解决了。
最后分享一个小技巧:在调试图形问题时,可以先用WINEDEBUG=+d3d环境变量打开详细日志,这样能看到 D3D 调用的完整流程,快速定位是哪个环节出了问题。这个技巧在排查黑屏和纹理错误时特别有用。